mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
feat(tools): added 200+ new tools across confluence, discord, exa, firecrawl, jina, jira, linear, linkup, MS suite, parallel, reddit, supabase, & tavily (#1824)
* feat(tools): added 150+ new tools across confluence, discord, exa, firecrawl, jina, jira, linear, linkup, MS suite, parallel, reddit, supabase, & tavily * feat(tools): added 150+ new tools across confluence, discord, exa, firecrawl, jina, jira, linear, linkup, MS suite, parallel, reddit, supabase, & tavily * replace console.log and console.error with loggers instead * cleanup * update message change * removed layout from all blocks with new operations * cleanup * fixed create row * fixed full text search * fixed firecrawl, fixed supabase download * fix subblock name * teams update * finish supabase * tested & updated parallel AI * removed dates from Exa, fixed research * fix jina * more jina updates * fixed tavily + tool-inp for empty dropdown items * updated microsoft tools * edited reddit * added html/text options for gmail outlook resend tools, added linear and jira triggers * reddit tool fix * another reddit fix * remove unused github utils, fix linear triggers * added onedrive delete, fixed jira tools * fixed confluence * fix some linear operations, fixed jira triggers * fix some linear tools * fix build * fix jira payloads * fixe jira payload * fix conflicts * added sample payload * fix stripe icon * run lint
This commit is contained in:
@@ -280,28 +280,6 @@ Delete an attachment from a Confluence page (moves to trash).
|
||||
| `attachmentId` | string | Deleted attachment ID |
|
||||
| `deleted` | boolean | Deletion status |
|
||||
|
||||
### `confluence_add_label`
|
||||
|
||||
Add a label to a Confluence page.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Confluence domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `pageId` | string | Yes | Confluence page ID to add label to |
|
||||
| `labelName` | string | Yes | Label name to add |
|
||||
| `cloudId` | string | No | Confluence Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of operation |
|
||||
| `pageId` | string | Page ID |
|
||||
| `labelName` | string | Label name |
|
||||
| `added` | boolean | Addition status |
|
||||
|
||||
### `confluence_list_labels`
|
||||
|
||||
List all labels on a Confluence page.
|
||||
@@ -321,28 +299,6 @@ List all labels on a Confluence page.
|
||||
| `ts` | string | Timestamp of retrieval |
|
||||
| `labels` | array | List of labels |
|
||||
|
||||
### `confluence_remove_label`
|
||||
|
||||
Remove a label from a Confluence page.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Confluence domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `pageId` | string | Yes | Confluence page ID to remove label from |
|
||||
| `labelName` | string | Yes | Label name to remove |
|
||||
| `cloudId` | string | No | Confluence Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of operation |
|
||||
| `pageId` | string | Page ID |
|
||||
| `labelName` | string | Label name |
|
||||
| `removed` | boolean | Removal status |
|
||||
|
||||
### `confluence_get_space`
|
||||
|
||||
Get details about a specific Confluence space.
|
||||
|
||||
@@ -64,10 +64,6 @@ Search the web using Exa AI. Returns relevant search results with titles, URLs,
|
||||
| `type` | string | No | Search type: neural, keyword, auto or fast \(default: auto\) |
|
||||
| `includeDomains` | string | No | Comma-separated list of domains to include in results |
|
||||
| `excludeDomains` | string | No | Comma-separated list of domains to exclude from results |
|
||||
| `startPublishedDate` | string | No | Filter results published after this date \(ISO 8601 format, e.g., 2024-01-01\) |
|
||||
| `endPublishedDate` | string | No | Filter results published before this date \(ISO 8601 format\) |
|
||||
| `startCrawlDate` | string | No | Filter results crawled after this date \(ISO 8601 format\) |
|
||||
| `endCrawlDate` | string | No | Filter results crawled before this date \(ISO 8601 format\) |
|
||||
| `category` | string | No | Filter by category: company, research_paper, news_article, pdf, github, tweet, movie, song, personal_site |
|
||||
| `text` | boolean | No | Include full text content in results \(default: false\) |
|
||||
| `highlights` | boolean | No | Include highlighted snippets in results \(default: false\) |
|
||||
@@ -118,10 +114,6 @@ Find webpages similar to a given URL using Exa AI. Returns a list of similar lin
|
||||
| `includeDomains` | string | No | Comma-separated list of domains to include in results |
|
||||
| `excludeDomains` | string | No | Comma-separated list of domains to exclude from results |
|
||||
| `excludeSourceDomain` | boolean | No | Exclude the source domain from results \(default: false\) |
|
||||
| `startPublishedDate` | string | No | Filter results published after this date \(ISO 8601 format, e.g., 2024-01-01\) |
|
||||
| `endPublishedDate` | string | No | Filter results published before this date \(ISO 8601 format\) |
|
||||
| `startCrawlDate` | string | No | Filter results crawled after this date \(ISO 8601 format\) |
|
||||
| `endCrawlDate` | string | No | Filter results crawled before this date \(ISO 8601 format\) |
|
||||
| `category` | string | No | Filter by category: company, research_paper, news_article, pdf, github, tweet, movie, song, personal_site |
|
||||
| `highlights` | boolean | No | Include highlighted snippets in results \(default: false\) |
|
||||
| `summary` | boolean | No | Include AI-generated summaries in results \(default: false\) |
|
||||
|
||||
@@ -59,7 +59,7 @@ This allows your agents to gather information from websites, extract structured
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Firecrawl into the workflow. Can scrape pages, search the web, crawl entire websites, map URL structures, and extract structured data using AI.
|
||||
Integrate Firecrawl into the workflow. Scrape pages, search the web, crawl entire sites, map URL structures, and extract structured data with AI.
|
||||
|
||||
|
||||
|
||||
@@ -74,25 +74,7 @@ Extract structured content from web pages with comprehensive metadata support. C
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `url` | string | Yes | The URL to scrape content from |
|
||||
| `formats` | json | No | Output formats \(markdown, html, rawHtml, links, images, screenshot\). Default: \["markdown"\] |
|
||||
| `onlyMainContent` | boolean | No | Extract only main content, excluding headers, navs, footers \(default: true\) |
|
||||
| `includeTags` | json | No | HTML tags to retain in the output |
|
||||
| `excludeTags` | json | No | HTML tags to remove from the output |
|
||||
| `maxAge` | number | No | Return cached version if younger than this age in ms \(default: 172800000\) |
|
||||
| `headers` | json | No | Custom request headers \(cookies, user-agent, etc.\) |
|
||||
| `waitFor` | number | No | Delay in milliseconds before fetching \(default: 0\) |
|
||||
| `mobile` | boolean | No | Emulate mobile device \(default: false\) |
|
||||
| `skipTlsVerification` | boolean | No | Skip TLS certificate verification \(default: true\) |
|
||||
| `timeout` | number | No | Request timeout in milliseconds |
|
||||
| `parsers` | json | No | File processing controls \(e.g., \["pdf"\]\) |
|
||||
| `actions` | json | No | Pre-scrape operations \(wait, click, scroll, screenshot, etc.\) |
|
||||
| `location` | json | No | Geographic settings \(country, languages\) |
|
||||
| `removeBase64Images` | boolean | No | Strip base64 images from output \(default: true\) |
|
||||
| `blockAds` | boolean | No | Enable ad and popup blocking \(default: true\) |
|
||||
| `proxy` | string | No | Proxy type: basic, stealth, or auto \(default: auto\) |
|
||||
| `storeInCache` | boolean | No | Cache the page \(default: true\) |
|
||||
| `zeroDataRetention` | boolean | No | Enable zero data retention mode \(default: false\) |
|
||||
| `scrapeOptions` | json | No | Options for content scraping \(legacy, prefer top-level params\) |
|
||||
| `scrapeOptions` | json | No | Options for content scraping |
|
||||
| `apiKey` | string | Yes | Firecrawl API key |
|
||||
|
||||
#### Output
|
||||
@@ -112,15 +94,6 @@ Search for information on the web using Firecrawl
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `query` | string | Yes | The search query to use |
|
||||
| `limit` | number | No | Maximum number of results to return \(1-100, default: 5\) |
|
||||
| `sources` | json | No | Search sources: \["web"\], \["images"\], or \["news"\] \(default: \["web"\]\) |
|
||||
| `categories` | json | No | Filter by categories: \["github"\], \["research"\], or \["pdf"\] |
|
||||
| `tbs` | string | No | Time-based search: qdr:h \(hour\), qdr:d \(day\), qdr:w \(week\), qdr:m \(month\), qdr:y \(year\) |
|
||||
| `location` | string | No | Geographic location for results \(e.g., "San Francisco, California, United States"\) |
|
||||
| `country` | string | No | ISO country code for geo-targeting \(default: US\) |
|
||||
| `timeout` | number | No | Timeout in milliseconds \(default: 60000\) |
|
||||
| `ignoreInvalidURLs` | boolean | No | Exclude invalid URLs from results \(default: false\) |
|
||||
| `scrapeOptions` | json | No | Advanced scraping configuration for search results |
|
||||
| `apiKey` | string | Yes | Firecrawl API key |
|
||||
|
||||
#### Output
|
||||
@@ -140,20 +113,6 @@ Crawl entire websites and extract structured content from all accessible pages
|
||||
| `url` | string | Yes | The website URL to crawl |
|
||||
| `limit` | number | No | Maximum number of pages to crawl \(default: 100\) |
|
||||
| `onlyMainContent` | boolean | No | Extract only main content from pages |
|
||||
| `prompt` | string | No | Natural language instruction to auto-generate crawler options |
|
||||
| `maxDiscoveryDepth` | number | No | Depth limit for URL discovery \(root pages have depth 0\) |
|
||||
| `sitemap` | string | No | Whether to use sitemap data: "skip" or "include" \(default: "include"\) |
|
||||
| `crawlEntireDomain` | boolean | No | Follow sibling/parent URLs or only child paths \(default: false\) |
|
||||
| `allowExternalLinks` | boolean | No | Follow external website links \(default: false\) |
|
||||
| `allowSubdomains` | boolean | No | Follow subdomain links \(default: false\) |
|
||||
| `ignoreQueryParameters` | boolean | No | Prevent re-scraping same path with different query params \(default: false\) |
|
||||
| `delay` | number | No | Seconds between scrapes for rate limit compliance |
|
||||
| `maxConcurrency` | number | No | Concurrent scrape limit |
|
||||
| `excludePaths` | json | No | Array of regex patterns for URLs to exclude |
|
||||
| `includePaths` | json | No | Array of regex patterns for URLs to include exclusively |
|
||||
| `webhook` | json | No | Webhook configuration for crawl notifications |
|
||||
| `scrapeOptions` | json | No | Advanced scraping configuration |
|
||||
| `zeroDataRetention` | boolean | No | Enable zero data retention \(default: false\) |
|
||||
| `apiKey` | string | Yes | Firecrawl API Key |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -76,6 +76,7 @@ Send emails using Gmail
|
||||
| `to` | string | Yes | Recipient email address |
|
||||
| `subject` | string | No | Email subject |
|
||||
| `body` | string | Yes | Email body content |
|
||||
| `contentType` | string | No | Content type for the email body \(text or html\) |
|
||||
| `threadId` | string | No | Thread ID to reply to \(for threading\) |
|
||||
| `replyToMessageId` | string | No | Gmail message ID to reply to - use the "id" field from Gmail Read results \(not the RFC "messageId"\) |
|
||||
| `cc` | string | No | CC recipients \(comma-separated\) |
|
||||
@@ -100,6 +101,7 @@ Draft emails using Gmail
|
||||
| `to` | string | Yes | Recipient email address |
|
||||
| `subject` | string | No | Email subject |
|
||||
| `body` | string | Yes | Email body content |
|
||||
| `contentType` | string | No | Content type for the email body \(text or html\) |
|
||||
| `threadId` | string | No | Thread ID to reply to \(for threading\) |
|
||||
| `replyToMessageId` | string | No | Gmail message ID to reply to - use the "id" field from Gmail Read results \(not the RFC "messageId"\) |
|
||||
| `cc` | string | No | CC recipients \(comma-separated\) |
|
||||
|
||||
@@ -82,25 +82,13 @@ Extract and process web content into clean, LLM-friendly text using Jina AI Read
|
||||
| `gatherLinks` | boolean | No | Whether to gather all links at the end |
|
||||
| `jsonResponse` | boolean | No | Whether to return response in JSON format |
|
||||
| `apiKey` | string | Yes | Your Jina AI API key |
|
||||
| `targetSelector` | string | No | CSS selector to target specific page elements \(e.g., "#main-content"\) |
|
||||
| `waitForSelector` | string | No | CSS selector to wait for before extracting content \(useful for dynamic pages\) |
|
||||
| `removeSelector` | string | No | CSS selector for elements to exclude \(e.g., "header, footer, .ad"\) |
|
||||
| `timeout` | number | No | Maximum seconds to wait for page load |
|
||||
| `withImagesummary` | boolean | No | Gather all images from the page with metadata |
|
||||
| `retainImages` | string | No | Control image inclusion: "none" removes all, "all" keeps all |
|
||||
| `returnFormat` | string | No | Output format: markdown, html, text, screenshot, or pageshot |
|
||||
| `withIframe` | boolean | No | Include iframe content in extraction |
|
||||
| `withShadowDom` | boolean | No | Extract Shadow DOM content |
|
||||
| `setCookie` | string | No | Forward authentication cookies \(disables caching\) |
|
||||
| `proxyUrl` | string | No | HTTP proxy URL for request routing |
|
||||
| `proxy` | string | No | Country code for proxy \(e.g., "US", "UK"\) or "auto"/"none" |
|
||||
| `engine` | string | No | Rendering engine: browser, direct, or cf-browser-rendering |
|
||||
| `tokenBudget` | number | No | Maximum tokens for the request \(cost control\) |
|
||||
| `noCache` | boolean | No | Bypass cached content for real-time retrieval |
|
||||
| `cacheTolerance` | number | No | Custom cache lifetime in seconds |
|
||||
| `withGeneratedAlt` | boolean | No | Generate alt text for images using VLM |
|
||||
| `baseUrl` | string | No | Set to "final" to follow redirect chain |
|
||||
| `locale` | string | No | Browser locale for rendering \(e.g., "en-US"\) |
|
||||
| `robotsTxt` | string | No | Bot User-Agent for robots.txt checking |
|
||||
| `dnt` | boolean | No | Do Not Track - prevents caching/tracking |
|
||||
| `noGfm` | boolean | No | Disable GitHub Flavored Markdown |
|
||||
@@ -123,11 +111,7 @@ Search the web and return top 5 results with LLM-friendly content. Each result i
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `q` | string | Yes | Search query string |
|
||||
| `apiKey` | string | Yes | Your Jina AI API key |
|
||||
| `gl` | string | No | Two-letter country code for geo-specific results \(e.g., "US", "UK", "JP"\) |
|
||||
| `location` | string | No | City-level location for localized search results |
|
||||
| `hl` | string | No | Two-letter language code for results \(e.g., "en", "es", "fr"\) |
|
||||
| `num` | number | No | Maximum number of results per page \(default: 5\) |
|
||||
| `page` | number | No | Page number for pagination \(offset\) |
|
||||
| `site` | string | No | Restrict results to specific domain\(s\). Can be comma-separated for multiple sites \(e.g., "jina.ai,github.com"\) |
|
||||
| `withFavicon` | boolean | No | Include website favicons in results |
|
||||
| `withImagesummary` | boolean | No | Gather all images from result pages with metadata |
|
||||
@@ -137,11 +121,6 @@ Search the web and return top 5 results with LLM-friendly content. Each result i
|
||||
| `withGeneratedAlt` | boolean | No | Generate alt text for images using VLM |
|
||||
| `respondWith` | string | No | Set to "no-content" to get only metadata without page content |
|
||||
| `returnFormat` | string | No | Output format: markdown, html, text, screenshot, or pageshot |
|
||||
| `engine` | string | No | Rendering engine: browser or direct |
|
||||
| `timeout` | number | No | Maximum seconds to wait for page load |
|
||||
| `setCookie` | string | No | Forward authentication cookies |
|
||||
| `proxyUrl` | string | No | HTTP proxy URL for request routing |
|
||||
| `locale` | string | No | Browser locale for rendering \(e.g., "en-US"\) |
|
||||
|
||||
#### Output
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ In Sim, the Jira integration allows your agents to seamlessly interact with your
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Jira into the workflow. Can read, write, and update issues.
|
||||
Integrate Jira into the workflow. Can read, write, and update issues. Can also trigger workflows based on Jira webhook events.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ In Sim, the Linear integration allows your agents to seamlessly interact with yo
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Linear into the workflow. Can manage issues, comments, projects, labels, workflow states, cycles, attachments, and more.
|
||||
Integrate Linear into the workflow. Can manage issues, comments, projects, labels, workflow states, cycles, attachments, and more. Can also trigger workflows based on Linear webhook events.
|
||||
|
||||
|
||||
|
||||
@@ -891,6 +891,590 @@ Mark a notification as read or unread in Linear
|
||||
| --------- | ---- | ----------- |
|
||||
| `notification` | object | The updated notification |
|
||||
|
||||
### `linear_create_customer`
|
||||
|
||||
Create a new customer in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `name` | string | Yes | Customer name |
|
||||
| `domains` | array | No | Domains associated with this customer |
|
||||
| `externalIds` | array | No | External IDs from other systems |
|
||||
| `logoUrl` | string | No | Customer's logo URL |
|
||||
| `ownerId` | string | No | ID of the user who owns this customer |
|
||||
| `revenue` | number | No | Annual revenue from this customer |
|
||||
| `size` | number | No | Size of the customer organization |
|
||||
| `statusId` | string | No | Customer status ID |
|
||||
| `tierId` | string | No | Customer tier ID |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `customer` | object | The created customer |
|
||||
|
||||
### `linear_list_customers`
|
||||
|
||||
List all customers in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `first` | number | No | Number of customers to return \(default: 50\) |
|
||||
| `after` | string | No | Cursor for pagination |
|
||||
| `includeArchived` | boolean | No | Include archived customers \(default: false\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `customers` | array | Array of customers |
|
||||
|
||||
### `linear_create_customer_request`
|
||||
|
||||
Create a customer request (need) in Linear. Assign to customer, set urgency (priority: 0 = Not important, 1 = Important), and optionally link to an issue.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `customerId` | string | Yes | Customer ID to assign this request to |
|
||||
| `body` | string | No | Description of the customer request |
|
||||
| `priority` | number | No | Urgency level: 0 = Not important, 1 = Important \(default: 0\) |
|
||||
| `issueId` | string | No | Issue ID to link this request to |
|
||||
| `projectId` | string | No | Project ID to link this request to |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `customerNeed` | object | The created customer request |
|
||||
|
||||
### `linear_update_customer_request`
|
||||
|
||||
Update a customer request (need) in Linear. Can change urgency, description, customer assignment, and linked issue.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `customerNeedId` | string | Yes | Customer request ID to update |
|
||||
| `body` | string | No | Updated description of the customer request |
|
||||
| `priority` | number | No | Updated urgency level: 0 = Not important, 1 = Important |
|
||||
| `customerId` | string | No | New customer ID to assign this request to |
|
||||
| `issueId` | string | No | New issue ID to link this request to |
|
||||
| `projectId` | string | No | New project ID to link this request to |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `customerNeed` | object | The updated customer request |
|
||||
|
||||
### `linear_list_customer_requests`
|
||||
|
||||
List all customer requests (needs) in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `first` | number | No | Number of customer requests to return \(default: 50\) |
|
||||
| `after` | string | No | Cursor for pagination |
|
||||
| `includeArchived` | boolean | No | Include archived customer requests \(default: false\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `customerNeeds` | array | Array of customer requests |
|
||||
|
||||
### `linear_get_customer`
|
||||
|
||||
Get a single customer by ID in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `customerId` | string | Yes | Customer ID to retrieve |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `customer` | object | The customer data |
|
||||
|
||||
### `linear_update_customer`
|
||||
|
||||
Update a customer in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `customerId` | string | Yes | Customer ID to update |
|
||||
| `name` | string | No | Updated customer name |
|
||||
| `domains` | array | No | Updated domains |
|
||||
| `externalIds` | array | No | Updated external IDs |
|
||||
| `logoUrl` | string | No | Updated logo URL |
|
||||
| `ownerId` | string | No | Updated owner user ID |
|
||||
| `revenue` | number | No | Updated annual revenue |
|
||||
| `size` | number | No | Updated organization size |
|
||||
| `statusId` | string | No | Updated customer status ID |
|
||||
| `tierId` | string | No | Updated customer tier ID |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `customer` | object | The updated customer |
|
||||
|
||||
### `linear_delete_customer`
|
||||
|
||||
Delete a customer in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `customerId` | string | Yes | Customer ID to delete |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the deletion was successful |
|
||||
|
||||
### `linear_merge_customers`
|
||||
|
||||
Merge two customers in Linear by moving all data from source to target
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `sourceCustomerId` | string | Yes | Source customer ID \(will be deleted after merge\) |
|
||||
| `targetCustomerId` | string | Yes | Target customer ID \(will receive all data\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `customer` | object | The merged target customer |
|
||||
|
||||
### `linear_create_customer_status`
|
||||
|
||||
Create a new customer status in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `name` | string | Yes | Customer status name |
|
||||
| `color` | string | Yes | Status color \(hex code\) |
|
||||
| `displayName` | string | No | Display name for the status |
|
||||
| `description` | string | No | Status description |
|
||||
| `position` | number | No | Position in status list |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `customerStatus` | object | The created customer status |
|
||||
|
||||
### `linear_update_customer_status`
|
||||
|
||||
Update a customer status in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `statusId` | string | Yes | Customer status ID to update |
|
||||
| `name` | string | No | Updated status name |
|
||||
| `color` | string | No | Updated status color |
|
||||
| `displayName` | string | No | Updated display name |
|
||||
| `description` | string | No | Updated description |
|
||||
| `position` | number | No | Updated position |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `customerStatus` | object | The updated customer status |
|
||||
|
||||
### `linear_delete_customer_status`
|
||||
|
||||
Delete a customer status in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `statusId` | string | Yes | Customer status ID to delete |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the deletion was successful |
|
||||
|
||||
### `linear_list_customer_statuses`
|
||||
|
||||
List all customer statuses in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `customerStatuses` | array | List of customer statuses |
|
||||
|
||||
### `linear_create_customer_tier`
|
||||
|
||||
Create a new customer tier in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `name` | string | Yes | Customer tier name |
|
||||
| `color` | string | Yes | Tier color \(hex code\) |
|
||||
| `displayName` | string | No | Display name for the tier |
|
||||
| `description` | string | No | Tier description |
|
||||
| `position` | number | No | Position in tier list |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `customerTier` | object | The created customer tier |
|
||||
|
||||
### `linear_update_customer_tier`
|
||||
|
||||
Update a customer tier in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `tierId` | string | Yes | Customer tier ID to update |
|
||||
| `name` | string | No | Updated tier name |
|
||||
| `color` | string | No | Updated tier color |
|
||||
| `displayName` | string | No | Updated display name |
|
||||
| `description` | string | No | Updated description |
|
||||
| `position` | number | No | Updated position |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `customerTier` | object | The updated customer tier |
|
||||
|
||||
### `linear_delete_customer_tier`
|
||||
|
||||
Delete a customer tier in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `tierId` | string | Yes | Customer tier ID to delete |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the deletion was successful |
|
||||
|
||||
### `linear_list_customer_tiers`
|
||||
|
||||
List all customer tiers in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `customerTiers` | array | List of customer tiers |
|
||||
|
||||
### `linear_delete_project`
|
||||
|
||||
Delete a project in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Project ID to delete |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the deletion was successful |
|
||||
|
||||
### `linear_create_project_label`
|
||||
|
||||
Create a new project label in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `name` | string | Yes | Project label name |
|
||||
| `color` | string | No | Label color \(hex code\) |
|
||||
| `description` | string | No | Label description |
|
||||
| `isGroup` | boolean | No | Whether this is a label group |
|
||||
| `parentId` | string | No | Parent label group ID |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `projectLabel` | object | The created project label |
|
||||
|
||||
### `linear_update_project_label`
|
||||
|
||||
Update a project label in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `labelId` | string | Yes | Project label ID to update |
|
||||
| `name` | string | No | Updated label name |
|
||||
| `color` | string | No | Updated label color |
|
||||
| `description` | string | No | Updated description |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `projectLabel` | object | The updated project label |
|
||||
|
||||
### `linear_delete_project_label`
|
||||
|
||||
Delete a project label in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `labelId` | string | Yes | Project label ID to delete |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the deletion was successful |
|
||||
|
||||
### `linear_list_project_labels`
|
||||
|
||||
List all project labels in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | No | Optional project ID to filter labels for a specific project |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `projectLabels` | array | List of project labels |
|
||||
|
||||
### `linear_add_label_to_project`
|
||||
|
||||
Add a label to a project in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Project ID |
|
||||
| `labelId` | string | Yes | Label ID to add |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the label was added successfully |
|
||||
| `projectId` | string | The project ID |
|
||||
|
||||
### `linear_remove_label_from_project`
|
||||
|
||||
Remove a label from a project in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Project ID |
|
||||
| `labelId` | string | Yes | Label ID to remove |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the label was removed successfully |
|
||||
| `projectId` | string | The project ID |
|
||||
|
||||
### `linear_create_project_milestone`
|
||||
|
||||
Create a new project milestone in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Project ID |
|
||||
| `name` | string | Yes | Milestone name |
|
||||
| `description` | string | No | Milestone description |
|
||||
| `targetDate` | string | No | Target date \(ISO 8601\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `projectMilestone` | object | The created project milestone |
|
||||
|
||||
### `linear_update_project_milestone`
|
||||
|
||||
Update a project milestone in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `milestoneId` | string | Yes | Project milestone ID to update |
|
||||
| `name` | string | No | Updated milestone name |
|
||||
| `description` | string | No | Updated description |
|
||||
| `targetDate` | string | No | Updated target date \(ISO 8601\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `projectMilestone` | object | The updated project milestone |
|
||||
|
||||
### `linear_delete_project_milestone`
|
||||
|
||||
Delete a project milestone in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `milestoneId` | string | Yes | Project milestone ID to delete |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the deletion was successful |
|
||||
|
||||
### `linear_list_project_milestones`
|
||||
|
||||
List all milestones for a project in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Project ID to list milestones for |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `projectMilestones` | array | List of project milestones |
|
||||
|
||||
### `linear_create_project_status`
|
||||
|
||||
Create a new project status in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `name` | string | Yes | Project status name |
|
||||
| `color` | string | Yes | Status color \(hex code\) |
|
||||
| `description` | string | No | Status description |
|
||||
| `indefinite` | boolean | No | Whether the status is indefinite |
|
||||
| `position` | number | No | Position in status list |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `projectStatus` | object | The created project status |
|
||||
|
||||
### `linear_update_project_status`
|
||||
|
||||
Update a project status in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `statusId` | string | Yes | Project status ID to update |
|
||||
| `name` | string | No | Updated status name |
|
||||
| `color` | string | No | Updated status color |
|
||||
| `description` | string | No | Updated description |
|
||||
| `indefinite` | boolean | No | Updated indefinite flag |
|
||||
| `position` | number | No | Updated position |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `projectStatus` | object | The updated project status |
|
||||
|
||||
### `linear_delete_project_status`
|
||||
|
||||
Delete a project status in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `statusId` | string | Yes | Project status ID to delete |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the deletion was successful |
|
||||
|
||||
### `linear_list_project_statuses`
|
||||
|
||||
List all project statuses in Linear
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `projectStatuses` | array | List of project statuses |
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
@@ -193,7 +193,10 @@ Update a task in Microsoft Planner
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the task was updated successfully |
|
||||
| `message` | string | Success message when task is updated |
|
||||
| `task` | object | The updated task object with all properties |
|
||||
| `taskId` | string | ID of the updated task |
|
||||
| `etag` | string | New ETag after update - use this for subsequent operations |
|
||||
| `metadata` | object | Metadata including taskId, planId, and taskUrl |
|
||||
|
||||
### `microsoft_planner_delete_task`
|
||||
@@ -217,21 +220,20 @@ Delete a task from Microsoft Planner
|
||||
|
||||
### `microsoft_planner_list_plans`
|
||||
|
||||
List all plans in a Microsoft 365 group
|
||||
List all plans shared with the current user
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `groupId` | string | Yes | The ID of the Microsoft 365 group |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether plans were retrieved successfully |
|
||||
| `plans` | array | Array of plan objects |
|
||||
| `metadata` | object | Metadata including groupId and count |
|
||||
| `plans` | array | Array of plan objects shared with the current user |
|
||||
| `metadata` | object | Metadata including userId and count |
|
||||
|
||||
### `microsoft_planner_read_plan`
|
||||
|
||||
@@ -361,6 +363,7 @@ Get detailed information about a task including checklist and references
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the task details were retrieved successfully |
|
||||
| `taskDetails` | object | The task details including description, checklist, and references |
|
||||
| `etag` | string | The ETag value for this task details - use this for update operations |
|
||||
| `metadata` | object | Metadata including taskId |
|
||||
|
||||
### `microsoft_planner_update_task_details`
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: OneDrive
|
||||
description: Create, upload, and list files
|
||||
description: Create, upload, download, list, and delete files
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
@@ -51,7 +51,7 @@ In Sim, the OneDrive integration enables your agents to directly interact with y
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate OneDrive into the workflow. Can create text and Excel files, upload files, and list files.
|
||||
Integrate OneDrive into the workflow. Can create text and Excel files, upload files, download files, list files, and delete files or folders.
|
||||
|
||||
|
||||
|
||||
@@ -136,6 +136,24 @@ List files and folders in OneDrive
|
||||
| `files` | array | Array of file and folder objects with metadata |
|
||||
| `nextPageToken` | string | Token for retrieving the next page of results \(optional\) |
|
||||
|
||||
### `onedrive_delete`
|
||||
|
||||
Delete a file or folder from OneDrive
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `fileId` | string | Yes | The ID of the file or folder to delete |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the file was deleted successfully |
|
||||
| `deleted` | boolean | Confirmation that the file was deleted |
|
||||
| `fileId` | string | The ID of the deleted file |
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
@@ -165,6 +165,7 @@ Send emails using Outlook
|
||||
| `to` | string | Yes | Recipient email address |
|
||||
| `subject` | string | Yes | Email subject |
|
||||
| `body` | string | Yes | Email body content |
|
||||
| `contentType` | string | No | Content type for the email body \(text or html\) |
|
||||
| `replyToMessageId` | string | No | Message ID to reply to \(for threading\) |
|
||||
| `conversationId` | string | No | Conversation ID for threading |
|
||||
| `cc` | string | No | CC recipients \(comma-separated\) |
|
||||
@@ -191,6 +192,7 @@ Draft emails using Outlook
|
||||
| `to` | string | Yes | Recipient email address |
|
||||
| `subject` | string | Yes | Email subject |
|
||||
| `body` | string | Yes | Email body content |
|
||||
| `contentType` | string | No | Content type for the email body \(text or html\) |
|
||||
| `cc` | string | No | CC recipients \(comma-separated\) |
|
||||
| `bcc` | string | No | BCC recipients \(comma-separated\) |
|
||||
| `attachments` | file[] | No | Files to attach to the email draft |
|
||||
|
||||
@@ -128,7 +128,6 @@ Conduct comprehensive deep research across the web using Parallel AI. Synthesize
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `input` | string | Yes | Research query or question \(up to 15,000 characters\) |
|
||||
| `processor` | string | No | Compute level: base, lite, pro, ultra, ultra2x, ultra4x, ultra8x \(default: base\) |
|
||||
| `output_schema` | string | No | Desired output format description. Use "text" for markdown reports with citations, or describe structured JSON format |
|
||||
| `include_domains` | string | No | Comma-separated list of domains to restrict research to \(source policy\) |
|
||||
| `exclude_domains` | string | No | Comma-separated list of domains to exclude from research \(source policy\) |
|
||||
| `apiKey` | string | Yes | Parallel AI API Key |
|
||||
|
||||
@@ -123,29 +123,6 @@ Fetch controversial posts from a subreddit
|
||||
| `subreddit` | string | Name of the subreddit where posts were fetched from |
|
||||
| `posts` | array | Array of controversial posts with title, author, URL, score, comments count, and metadata |
|
||||
|
||||
### `reddit_get_gilded`
|
||||
|
||||
Fetch gilded/awarded posts from a subreddit
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `subreddit` | string | Yes | The name of the subreddit to fetch posts from \(without the r/ prefix\) |
|
||||
| `limit` | number | No | Maximum number of posts to return \(default: 10, max: 100\) |
|
||||
| `after` | string | No | Fullname of a thing to fetch items after \(for pagination\) |
|
||||
| `before` | string | No | Fullname of a thing to fetch items before \(for pagination\) |
|
||||
| `count` | number | No | A count of items already seen in the listing \(used for numbering\) |
|
||||
| `show` | string | No | Show items that would normally be filtered \(e.g., "all"\) |
|
||||
| `sr_detail` | boolean | No | Expand subreddit details in the response |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `subreddit` | string | Name of the subreddit where posts were fetched from |
|
||||
| `posts` | array | Array of gilded/awarded posts with title, author, URL, score, comments count, and metadata |
|
||||
|
||||
### `reddit_search`
|
||||
|
||||
Search for posts within a subreddit
|
||||
|
||||
@@ -59,6 +59,7 @@ Send an email using your own Resend API key and from address
|
||||
| `to` | string | Yes | Recipient email address |
|
||||
| `subject` | string | Yes | Email subject |
|
||||
| `body` | string | Yes | Email body content |
|
||||
| `contentType` | string | No | Content type for the email body \(text or html\) |
|
||||
| `resendApiKey` | string | Yes | Resend API key for sending emails |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -37,13 +37,13 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
/>
|
||||
<path d='M194.9 33.9001H169.8V121.4H194.9V33.9001Z' fill='white' />
|
||||
<path
|
||||
fill-rule='evenodd'
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
d='M142.9 41.3001L141.3 33.9001H119.7V121.4H144.7V62.1001C150.6 54.4001 160.6 55.8001 163.7 56.9001V33.9001C160.5 32.7001 148.8 30.5001 142.9 41.3001Z'
|
||||
fill='white'
|
||||
/>
|
||||
<path
|
||||
fill-rule='evenodd'
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
d='M92.8999 12.2002L68.4999 17.4002L68.3999 97.5002C68.3999 112.3 79.4999 123.2 94.2999 123.2C102.5 123.2 108.5 121.7 111.8 119.9V99.6002C108.6 100.9 92.7999 105.5 92.7999 90.7002V55.2002H111.8V33.9002H92.7999L92.8999 12.2002Z'
|
||||
fill='white'
|
||||
|
||||
@@ -325,16 +325,14 @@ Download a file from a Supabase storage bucket
|
||||
| `projectId` | string | Yes | Your Supabase project ID \(e.g., jdrkgepadsdopsntdlom\) |
|
||||
| `bucket` | string | Yes | The name of the storage bucket |
|
||||
| `path` | string | Yes | The path to the file to download \(e.g., "folder/file.jpg"\) |
|
||||
| `fileName` | string | No | Optional filename override |
|
||||
| `apiKey` | string | Yes | Your Supabase service role secret key |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Operation status message |
|
||||
| `fileContent` | string | File content \(base64 encoded if binary, plain text otherwise\) |
|
||||
| `contentType` | string | MIME type of the file |
|
||||
| `isBase64` | boolean | Whether the file content is base64 encoded |
|
||||
| `file` | file | Downloaded file stored in execution files |
|
||||
|
||||
### `supabase_storage_list`
|
||||
|
||||
|
||||
@@ -228,7 +228,7 @@ export default function LoginPage({
|
||||
},
|
||||
{
|
||||
onError: (ctx) => {
|
||||
console.error('Login error:', ctx.error)
|
||||
logger.error('Login error:', ctx.error)
|
||||
const errorMessage: string[] = ['Invalid email or password']
|
||||
|
||||
if (ctx.error.code?.includes('EMAIL_NOT_VERIFIED')) {
|
||||
@@ -288,7 +288,7 @@ export default function LoginPage({
|
||||
return
|
||||
}
|
||||
|
||||
console.error('Uncaught login error:', err)
|
||||
logger.error('Uncaught login error:', err)
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
|
||||
@@ -14,10 +14,13 @@ import {
|
||||
} from '@/components/ui/select'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import { quickValidateEmail } from '@/lib/email/validation'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { LegalLayout } from '@/app/(landing)/components'
|
||||
import { soehne } from '@/app/fonts/soehne/soehne'
|
||||
|
||||
const logger = createLogger('CareersPage')
|
||||
|
||||
const validateName = (name: string): string[] => {
|
||||
const errors: string[] = []
|
||||
if (!name || name.trim().length < 2) {
|
||||
@@ -188,7 +191,7 @@ export default function CareersPage() {
|
||||
|
||||
setSubmitStatus('success')
|
||||
} catch (error) {
|
||||
console.error('Error submitting application:', error)
|
||||
logger.error('Error submitting application:', error)
|
||||
setSubmitStatus('error')
|
||||
} finally {
|
||||
setIsSubmitting(false)
|
||||
|
||||
@@ -325,7 +325,6 @@ export async function POST(req: NextRequest) {
|
||||
...(processedFileContents.length > 0 && { fileAttachments: processedFileContents }),
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
logger.info(`[${tracker.requestId}] About to call Sim Agent`, {
|
||||
hasContext: agentContexts.length > 0,
|
||||
|
||||
@@ -56,11 +56,6 @@ describe('File Upload API Route', () => {
|
||||
const response = await POST(req)
|
||||
const data = await response.json()
|
||||
|
||||
if (response.status !== 200) {
|
||||
console.error('Upload failed with status:', response.status)
|
||||
console.error('Error response:', data)
|
||||
}
|
||||
|
||||
expect(response.status).toBe(200)
|
||||
expect(data).toHaveProperty('url')
|
||||
expect(data.url).toMatch(/\/api\/files\/serve\/.*\.txt$/)
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/security/input-validation'
|
||||
import { getConfluenceCloudId } from '@/tools/confluence/utils'
|
||||
|
||||
const logger = createLogger('ConfluenceAttachmentAPI')
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
// Delete an attachment
|
||||
@@ -45,7 +48,7 @@ export async function DELETE(request: Request) {
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
console.error('Confluence API error response:', {
|
||||
logger.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
@@ -57,7 +60,7 @@ export async function DELETE(request: Request) {
|
||||
|
||||
return NextResponse.json({ attachmentId, deleted: true })
|
||||
} catch (error) {
|
||||
console.error('Error deleting Confluence attachment:', error)
|
||||
logger.error('Error deleting Confluence attachment:', error)
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message || 'Internal server error' },
|
||||
{ status: 500 }
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/security/input-validation'
|
||||
import { getConfluenceCloudId } from '@/tools/confluence/utils'
|
||||
|
||||
const logger = createLogger('ConfluenceAttachmentsAPI')
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
// List attachments on a page
|
||||
@@ -50,7 +53,7 @@ export async function GET(request: Request) {
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
console.error('Confluence API error response:', {
|
||||
logger.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
@@ -72,7 +75,7 @@ export async function GET(request: Request) {
|
||||
|
||||
return NextResponse.json({ attachments })
|
||||
} catch (error) {
|
||||
console.error('Error listing Confluence attachments:', error)
|
||||
logger.error('Error listing Confluence attachments:', error)
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message || 'Internal server error' },
|
||||
{ status: 500 }
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/security/input-validation'
|
||||
import { getConfluenceCloudId } from '@/tools/confluence/utils'
|
||||
|
||||
const logger = createLogger('ConfluenceCommentAPI')
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
// Update a comment
|
||||
@@ -84,7 +87,7 @@ export async function PUT(request: Request) {
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
console.error('Confluence API error response:', {
|
||||
logger.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
@@ -97,7 +100,7 @@ export async function PUT(request: Request) {
|
||||
const data = await response.json()
|
||||
return NextResponse.json(data)
|
||||
} catch (error) {
|
||||
console.error('Error updating Confluence comment:', error)
|
||||
logger.error('Error updating Confluence comment:', error)
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message || 'Internal server error' },
|
||||
{ status: 500 }
|
||||
@@ -146,7 +149,7 @@ export async function DELETE(request: Request) {
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
console.error('Confluence API error response:', {
|
||||
logger.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
@@ -158,7 +161,7 @@ export async function DELETE(request: Request) {
|
||||
|
||||
return NextResponse.json({ commentId, deleted: true })
|
||||
} catch (error) {
|
||||
console.error('Error deleting Confluence comment:', error)
|
||||
logger.error('Error deleting Confluence comment:', error)
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message || 'Internal server error' },
|
||||
{ status: 500 }
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/security/input-validation'
|
||||
import { getConfluenceCloudId } from '@/tools/confluence/utils'
|
||||
|
||||
const logger = createLogger('ConfluenceCommentsAPI')
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
// Create a comment
|
||||
@@ -39,6 +42,8 @@ export async function POST(request: Request) {
|
||||
|
||||
const url = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/api/v2/footer-comments`
|
||||
|
||||
logger.info('Calling Confluence API', { url })
|
||||
|
||||
const body = {
|
||||
pageId,
|
||||
body: {
|
||||
@@ -59,7 +64,7 @@ export async function POST(request: Request) {
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
console.error('Confluence API error response:', {
|
||||
logger.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
@@ -72,7 +77,7 @@ export async function POST(request: Request) {
|
||||
const data = await response.json()
|
||||
return NextResponse.json({ ...data, pageId })
|
||||
} catch (error) {
|
||||
console.error('Error creating Confluence comment:', error)
|
||||
logger.error('Error creating Confluence comment:', error)
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message || 'Internal server error' },
|
||||
{ status: 500 }
|
||||
@@ -126,7 +131,7 @@ export async function GET(request: Request) {
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
console.error('Confluence API error response:', {
|
||||
logger.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
@@ -147,7 +152,7 @@ export async function GET(request: Request) {
|
||||
|
||||
return NextResponse.json({ comments })
|
||||
} catch (error) {
|
||||
console.error('Error listing Confluence comments:', error)
|
||||
logger.error('Error listing Confluence comments:', error)
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message || 'Internal server error' },
|
||||
{ status: 500 }
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/security/input-validation'
|
||||
import { getConfluenceCloudId } from '@/tools/confluence/utils'
|
||||
|
||||
const logger = createLogger('ConfluenceCreatePageAPI')
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
export async function POST(request: Request) {
|
||||
@@ -65,7 +68,7 @@ export async function POST(request: Request) {
|
||||
},
|
||||
}
|
||||
|
||||
if (parentId) {
|
||||
if (parentId !== undefined && parentId !== null && parentId !== '') {
|
||||
createBody.parentId = parentId
|
||||
}
|
||||
|
||||
@@ -83,7 +86,7 @@ export async function POST(request: Request) {
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
console.error('Confluence API error response:', {
|
||||
logger.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
@@ -98,7 +101,7 @@ export async function POST(request: Request) {
|
||||
const data = await response.json()
|
||||
return NextResponse.json(data)
|
||||
} catch (error) {
|
||||
console.error('Error creating Confluence page:', error)
|
||||
logger.error('Error creating Confluence page:', error)
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message || 'Internal server error' },
|
||||
{ status: 500 }
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/security/input-validation'
|
||||
import { getConfluenceCloudId } from '@/tools/confluence/utils'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
// Remove a label from a page
|
||||
export async function DELETE(request: Request) {
|
||||
try {
|
||||
const {
|
||||
domain,
|
||||
accessToken,
|
||||
cloudId: providedCloudId,
|
||||
pageId,
|
||||
labelName,
|
||||
} = await request.json()
|
||||
|
||||
if (!domain) {
|
||||
return NextResponse.json({ error: 'Domain is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
if (!accessToken) {
|
||||
return NextResponse.json({ error: 'Access token is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
if (!pageId) {
|
||||
return NextResponse.json({ error: 'Page ID is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
if (!labelName) {
|
||||
return NextResponse.json({ error: 'Label name is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
const pageIdValidation = validateAlphanumericId(pageId, 'pageId', 255)
|
||||
if (!pageIdValidation.isValid) {
|
||||
return NextResponse.json({ error: pageIdValidation.error }, { status: 400 })
|
||||
}
|
||||
|
||||
const cloudId = providedCloudId || (await getConfluenceCloudId(domain, accessToken))
|
||||
|
||||
const cloudIdValidation = validateJiraCloudId(cloudId, 'cloudId')
|
||||
if (!cloudIdValidation.isValid) {
|
||||
return NextResponse.json({ error: cloudIdValidation.error }, { status: 400 })
|
||||
}
|
||||
|
||||
// First, get all labels to find the label ID
|
||||
const listUrl = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/api/v2/pages/${pageId}/labels`
|
||||
const listResponse = await fetch(listUrl, {
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (!listResponse.ok) {
|
||||
throw new Error(`Failed to list labels: ${listResponse.status}`)
|
||||
}
|
||||
|
||||
const listData = await listResponse.json()
|
||||
const label = (listData.results || []).find((l: any) => l.name === labelName)
|
||||
|
||||
if (!label) {
|
||||
return NextResponse.json({ error: `Label "${labelName}" not found on page` }, { status: 404 })
|
||||
}
|
||||
|
||||
const url = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/api/v2/pages/${pageId}/labels/${label.id}`
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
console.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
})
|
||||
const errorMessage =
|
||||
errorData?.message || `Failed to remove Confluence label (${response.status})`
|
||||
return NextResponse.json({ error: errorMessage }, { status: response.status })
|
||||
}
|
||||
|
||||
return NextResponse.json({ pageId, labelName, removed: true })
|
||||
} catch (error) {
|
||||
console.error('Error removing Confluence label:', error)
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message || 'Internal server error' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/security/input-validation'
|
||||
import { getConfluenceCloudId } from '@/tools/confluence/utils'
|
||||
|
||||
const logger = createLogger('ConfluenceLabelsAPI')
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
// Add a label to a page
|
||||
@@ -62,7 +65,7 @@ export async function POST(request: Request) {
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
console.error('Confluence API error response:', {
|
||||
logger.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
@@ -75,7 +78,7 @@ export async function POST(request: Request) {
|
||||
const data = await response.json()
|
||||
return NextResponse.json({ ...data, pageId, labelName })
|
||||
} catch (error) {
|
||||
console.error('Error adding Confluence label:', error)
|
||||
logger.error('Error adding Confluence label:', error)
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message || 'Internal server error' },
|
||||
{ status: 500 }
|
||||
@@ -128,7 +131,7 @@ export async function GET(request: Request) {
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
console.error('Confluence API error response:', {
|
||||
logger.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
@@ -148,7 +151,7 @@ export async function GET(request: Request) {
|
||||
|
||||
return NextResponse.json({ labels })
|
||||
} catch (error) {
|
||||
console.error('Error listing Confluence labels:', error)
|
||||
logger.error('Error listing Confluence labels:', error)
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message || 'Internal server error' },
|
||||
{ status: 500 }
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/security/input-validation'
|
||||
import { getConfluenceCloudId } from '@/tools/confluence/utils'
|
||||
|
||||
const logger = createLogger('ConfluencePageAPI')
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
export async function POST(request: Request) {
|
||||
@@ -43,15 +46,15 @@ export async function POST(request: Request) {
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
console.error(`Confluence API error: ${response.status} ${response.statusText}`)
|
||||
logger.error(`Confluence API error: ${response.status} ${response.statusText}`)
|
||||
let errorMessage
|
||||
|
||||
try {
|
||||
const errorData = await response.json()
|
||||
console.error('Error details:', JSON.stringify(errorData, null, 2))
|
||||
logger.error('Error details:', JSON.stringify(errorData, null, 2))
|
||||
errorMessage = errorData.message || `Failed to fetch Confluence page (${response.status})`
|
||||
} catch (e) {
|
||||
console.error('Could not parse error response as JSON:', e)
|
||||
logger.error('Could not parse error response as JSON:', e)
|
||||
errorMessage = `Failed to fetch Confluence page: ${response.status} ${response.statusText}`
|
||||
}
|
||||
|
||||
@@ -76,7 +79,7 @@ export async function POST(request: Request) {
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error fetching Confluence page:', error)
|
||||
logger.error('Error fetching Confluence page:', error)
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message || 'Internal server error' },
|
||||
{ status: 500 }
|
||||
@@ -143,14 +146,27 @@ export async function PUT(request: Request) {
|
||||
number: currentVersion + 1,
|
||||
message: version?.message || 'Updated via API',
|
||||
},
|
||||
title: title,
|
||||
body: {
|
||||
representation: 'storage',
|
||||
value: pageBody?.value || '',
|
||||
},
|
||||
status: 'current',
|
||||
}
|
||||
|
||||
if (title !== undefined && title !== null && title !== '') {
|
||||
updateBody.title = title
|
||||
} else {
|
||||
updateBody.title = currentPage.title
|
||||
}
|
||||
|
||||
if (pageBody?.value !== undefined && pageBody?.value !== null && pageBody?.value !== '') {
|
||||
updateBody.body = {
|
||||
representation: 'storage',
|
||||
value: pageBody.value,
|
||||
}
|
||||
} else {
|
||||
updateBody.body = {
|
||||
representation: 'storage',
|
||||
value: currentPage.body?.storage?.value || '',
|
||||
}
|
||||
}
|
||||
|
||||
const response = await fetch(currentPageUrl, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
@@ -163,7 +179,7 @@ export async function PUT(request: Request) {
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
console.error('Confluence API error response:', {
|
||||
logger.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
@@ -178,7 +194,7 @@ export async function PUT(request: Request) {
|
||||
const data = await response.json()
|
||||
return NextResponse.json(data)
|
||||
} catch (error) {
|
||||
console.error('Error updating Confluence page:', error)
|
||||
logger.error('Error updating Confluence page:', error)
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message || 'Internal server error' },
|
||||
{ status: 500 }
|
||||
@@ -226,7 +242,7 @@ export async function DELETE(request: Request) {
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
console.error('Confluence API error response:', {
|
||||
logger.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
@@ -238,7 +254,7 @@ export async function DELETE(request: Request) {
|
||||
|
||||
return NextResponse.json({ pageId, deleted: true })
|
||||
} catch (error) {
|
||||
console.error('Error deleting Confluence page:', error)
|
||||
logger.error('Error deleting Confluence page:', error)
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message || 'Internal server error' },
|
||||
{ status: 500 }
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { validateJiraCloudId } from '@/lib/security/input-validation'
|
||||
import { getConfluenceCloudId } from '@/tools/confluence/utils'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
const logger = createLogger('Confluence Search')
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const {
|
||||
@@ -50,7 +53,7 @@ export async function POST(request: Request) {
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
console.error('Confluence API error response:', {
|
||||
logger.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
@@ -71,7 +74,7 @@ export async function POST(request: Request) {
|
||||
|
||||
return NextResponse.json({ results })
|
||||
} catch (error) {
|
||||
console.error('Error searching Confluence:', error)
|
||||
logger.error('Error searching Confluence:', error)
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message || 'Internal server error' },
|
||||
{ status: 500 }
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/security/input-validation'
|
||||
import { getConfluenceCloudId } from '@/tools/confluence/utils'
|
||||
|
||||
const logger = createLogger('ConfluenceSpaceAPI')
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
// Get a specific space
|
||||
@@ -49,7 +52,7 @@ export async function GET(request: Request) {
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
console.error('Confluence API error response:', {
|
||||
logger.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
@@ -62,7 +65,7 @@ export async function GET(request: Request) {
|
||||
const data = await response.json()
|
||||
return NextResponse.json(data)
|
||||
} catch (error) {
|
||||
console.error('Error getting Confluence space:', error)
|
||||
logger.error('Error getting Confluence space:', error)
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message || 'Internal server error' },
|
||||
{ status: 500 }
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { validateJiraCloudId } from '@/lib/security/input-validation'
|
||||
import { getConfluenceCloudId } from '@/tools/confluence/utils'
|
||||
|
||||
const logger = createLogger('ConfluenceSpacesAPI')
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
// List all spaces
|
||||
@@ -40,7 +43,7 @@ export async function GET(request: Request) {
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
console.error('Confluence API error response:', {
|
||||
logger.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
@@ -62,7 +65,7 @@ export async function GET(request: Request) {
|
||||
|
||||
return NextResponse.json({ spaces })
|
||||
} catch (error) {
|
||||
console.error('Error listing Confluence spaces:', error)
|
||||
logger.error('Error listing Confluence spaces:', error)
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message || 'Internal server error' },
|
||||
{ status: 500 }
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ jobId: string }> }
|
||||
) {
|
||||
const { jobId } = await params
|
||||
const authHeader = request.headers.get('authorization')
|
||||
|
||||
if (!authHeader) {
|
||||
return NextResponse.json({ error: 'Authorization header is required' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`https://api.firecrawl.dev/v1/crawl/${jobId}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: authHeader,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
return NextResponse.json(
|
||||
{ error: data.error || data.message || 'Failed to get crawl status' },
|
||||
{ status: response.status }
|
||||
)
|
||||
}
|
||||
|
||||
return NextResponse.json(data)
|
||||
} catch (error: any) {
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to fetch crawl status: ${error.message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ const GmailDraftSchema = z.object({
|
||||
to: z.string().min(1, 'Recipient email is required'),
|
||||
subject: z.string().optional().nullable(),
|
||||
body: z.string().min(1, 'Email body is required'),
|
||||
contentType: z.enum(['text', 'html']).optional().nullable(),
|
||||
threadId: z.string().optional().nullable(),
|
||||
replyToMessageId: z.string().optional().nullable(),
|
||||
cc: z.string().optional().nullable(),
|
||||
@@ -123,6 +124,7 @@ export async function POST(request: NextRequest) {
|
||||
bcc: validatedData.bcc ?? undefined,
|
||||
subject: validatedData.subject || originalSubject || '',
|
||||
body: validatedData.body,
|
||||
contentType: validatedData.contentType || 'text',
|
||||
inReplyTo: originalMessageId,
|
||||
references: originalReferences,
|
||||
attachments: attachmentBuffers,
|
||||
@@ -140,6 +142,7 @@ export async function POST(request: NextRequest) {
|
||||
bcc: validatedData.bcc,
|
||||
subject: validatedData.subject || originalSubject,
|
||||
body: validatedData.body,
|
||||
contentType: validatedData.contentType || 'text',
|
||||
inReplyTo: originalMessageId,
|
||||
references: originalReferences,
|
||||
})
|
||||
|
||||
@@ -23,6 +23,7 @@ const GmailSendSchema = z.object({
|
||||
to: z.string().min(1, 'Recipient email is required'),
|
||||
subject: z.string().optional().nullable(),
|
||||
body: z.string().min(1, 'Email body is required'),
|
||||
contentType: z.enum(['text', 'html']).optional().nullable(),
|
||||
threadId: z.string().optional().nullable(),
|
||||
replyToMessageId: z.string().optional().nullable(),
|
||||
cc: z.string().optional().nullable(),
|
||||
@@ -123,6 +124,7 @@ export async function POST(request: NextRequest) {
|
||||
bcc: validatedData.bcc ?? undefined,
|
||||
subject: validatedData.subject || originalSubject || '',
|
||||
body: validatedData.body,
|
||||
contentType: validatedData.contentType || 'text',
|
||||
inReplyTo: originalMessageId,
|
||||
references: originalReferences,
|
||||
attachments: attachmentBuffers,
|
||||
@@ -140,6 +142,7 @@ export async function POST(request: NextRequest) {
|
||||
bcc: validatedData.bcc,
|
||||
subject: validatedData.subject || originalSubject,
|
||||
body: validatedData.body,
|
||||
contentType: validatedData.contentType || 'text',
|
||||
inReplyTo: originalMessageId,
|
||||
references: originalReferences,
|
||||
})
|
||||
|
||||
@@ -57,11 +57,11 @@ export async function PUT(request: Request) {
|
||||
const summaryValue = summary || title
|
||||
const fields: Record<string, any> = {}
|
||||
|
||||
if (summaryValue) {
|
||||
if (summaryValue !== undefined && summaryValue !== null && summaryValue !== '') {
|
||||
fields.summary = summaryValue
|
||||
}
|
||||
|
||||
if (description) {
|
||||
if (description !== undefined && description !== null && description !== '') {
|
||||
fields.description = {
|
||||
type: 'doc',
|
||||
version: 1,
|
||||
@@ -79,19 +79,19 @@ export async function PUT(request: Request) {
|
||||
}
|
||||
}
|
||||
|
||||
if (status) {
|
||||
if (status !== undefined && status !== null && status !== '') {
|
||||
fields.status = {
|
||||
name: status,
|
||||
}
|
||||
}
|
||||
|
||||
if (priority) {
|
||||
if (priority !== undefined && priority !== null && priority !== '') {
|
||||
fields.priority = {
|
||||
name: priority,
|
||||
}
|
||||
}
|
||||
|
||||
if (assignee) {
|
||||
if (assignee !== undefined && assignee !== null && assignee !== '') {
|
||||
fields.assignee = {
|
||||
id: assignee,
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ export async function POST(request: Request) {
|
||||
summary: summary,
|
||||
}
|
||||
|
||||
if (description) {
|
||||
if (description !== undefined && description !== null && description !== '') {
|
||||
fields.description = {
|
||||
type: 'doc',
|
||||
version: 1,
|
||||
@@ -89,17 +89,17 @@ export async function POST(request: Request) {
|
||||
}
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
if (parent !== undefined && parent !== null && parent !== '') {
|
||||
fields.parent = parent
|
||||
}
|
||||
|
||||
if (priority) {
|
||||
if (priority !== undefined && priority !== null && priority !== '') {
|
||||
fields.priority = {
|
||||
name: priority,
|
||||
}
|
||||
}
|
||||
|
||||
if (assignee) {
|
||||
if (assignee !== undefined && assignee !== null && assignee !== '') {
|
||||
fields.assignee = {
|
||||
id: assignee,
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ const MailSendSchema = z.object({
|
||||
to: z.string().email('Invalid email address').min(1, 'To email is required'),
|
||||
subject: z.string().min(1, 'Subject is required'),
|
||||
body: z.string().min(1, 'Email body is required'),
|
||||
contentType: z.enum(['text', 'html']).optional().nullable(),
|
||||
resendApiKey: z.string().min(1, 'Resend API key is required'),
|
||||
})
|
||||
|
||||
@@ -50,13 +51,22 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
const resend = new Resend(validatedData.resendApiKey)
|
||||
|
||||
const emailData = {
|
||||
from: validatedData.fromAddress,
|
||||
to: validatedData.to,
|
||||
subject: validatedData.subject,
|
||||
html: validatedData.body,
|
||||
text: validatedData.body.replace(/<[^>]*>/g, ''), // Strip HTML for text version
|
||||
}
|
||||
const contentType = validatedData.contentType || 'text'
|
||||
const emailData =
|
||||
contentType === 'html'
|
||||
? {
|
||||
from: validatedData.fromAddress,
|
||||
to: validatedData.to,
|
||||
subject: validatedData.subject,
|
||||
html: validatedData.body,
|
||||
text: validatedData.body.replace(/<[^>]*>/g, ''), // Strip HTML for text version
|
||||
}
|
||||
: {
|
||||
from: validatedData.fromAddress,
|
||||
to: validatedData.to,
|
||||
subject: validatedData.subject,
|
||||
text: validatedData.body,
|
||||
}
|
||||
|
||||
const { data, error } = await resend.emails.send(emailData)
|
||||
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { generateRequestId } from '@/lib/utils'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
const logger = createLogger('TeamsDeleteChatMessageAPI')
|
||||
|
||||
const TeamsDeleteChatMessageSchema = z.object({
|
||||
accessToken: z.string().min(1, 'Access token is required'),
|
||||
chatId: z.string().min(1, 'Chat ID is required'),
|
||||
messageId: z.string().min(1, 'Message ID is required'),
|
||||
})
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const requestId = generateRequestId()
|
||||
|
||||
try {
|
||||
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||
|
||||
if (!authResult.success) {
|
||||
logger.warn(`[${requestId}] Unauthorized Teams chat delete attempt: ${authResult.error}`)
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: authResult.error || 'Authentication required',
|
||||
},
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`[${requestId}] Authenticated Teams chat message delete request via ${authResult.authType}`,
|
||||
{
|
||||
userId: authResult.userId,
|
||||
}
|
||||
)
|
||||
|
||||
const body = await request.json()
|
||||
const validatedData = TeamsDeleteChatMessageSchema.parse(body)
|
||||
|
||||
logger.info(`[${requestId}] Deleting Teams chat message`, {
|
||||
chatId: validatedData.chatId,
|
||||
messageId: validatedData.messageId,
|
||||
})
|
||||
|
||||
// First, get the current user's ID (required for chat message deletion endpoint)
|
||||
const meUrl = 'https://graph.microsoft.com/v1.0/me'
|
||||
const meResponse = await fetch(meUrl, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${validatedData.accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (!meResponse.ok) {
|
||||
const errorData = await meResponse.json().catch(() => ({}))
|
||||
logger.error(`[${requestId}] Failed to get user ID:`, errorData)
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: errorData.error?.message || 'Failed to get user information',
|
||||
},
|
||||
{ status: meResponse.status }
|
||||
)
|
||||
}
|
||||
|
||||
const userData = await meResponse.json()
|
||||
const userId = userData.id
|
||||
|
||||
logger.info(`[${requestId}] Retrieved user ID: ${userId}`)
|
||||
|
||||
// Now perform the softDelete operation using the correct endpoint format
|
||||
const deleteUrl = `https://graph.microsoft.com/v1.0/users/${encodeURIComponent(userId)}/chats/${encodeURIComponent(validatedData.chatId)}/messages/${encodeURIComponent(validatedData.messageId)}/softDelete`
|
||||
|
||||
const deleteResponse = await fetch(deleteUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${validatedData.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({}), // softDelete requires an empty JSON body
|
||||
})
|
||||
|
||||
if (!deleteResponse.ok) {
|
||||
const errorData = await deleteResponse.json().catch(() => ({}))
|
||||
logger.error(`[${requestId}] Microsoft Teams API delete error:`, errorData)
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: errorData.error?.message || 'Failed to delete Teams message',
|
||||
},
|
||||
{ status: deleteResponse.status }
|
||||
)
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] Teams message deleted successfully`)
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
output: {
|
||||
deleted: true,
|
||||
messageId: validatedData.messageId,
|
||||
metadata: {
|
||||
messageId: validatedData.messageId,
|
||||
chatId: validatedData.chatId,
|
||||
},
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
logger.error(`[${requestId}] Error deleting Teams chat message:`, error)
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error occurred',
|
||||
},
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ const OutlookDraftSchema = z.object({
|
||||
to: z.string().min(1, 'Recipient email is required'),
|
||||
subject: z.string().min(1, 'Subject is required'),
|
||||
body: z.string().min(1, 'Email body is required'),
|
||||
contentType: z.enum(['text', 'html']).optional().nullable(),
|
||||
cc: z.string().optional().nullable(),
|
||||
bcc: z.string().optional().nullable(),
|
||||
attachments: z.array(z.any()).optional().nullable(),
|
||||
@@ -70,7 +71,7 @@ export async function POST(request: NextRequest) {
|
||||
const message: any = {
|
||||
subject: validatedData.subject,
|
||||
body: {
|
||||
contentType: 'Text',
|
||||
contentType: validatedData.contentType || 'text',
|
||||
content: validatedData.body,
|
||||
},
|
||||
toRecipients,
|
||||
|
||||
@@ -15,6 +15,7 @@ const OutlookSendSchema = z.object({
|
||||
to: z.string().min(1, 'Recipient email is required'),
|
||||
subject: z.string().min(1, 'Subject is required'),
|
||||
body: z.string().min(1, 'Email body is required'),
|
||||
contentType: z.enum(['text', 'html']).optional().nullable(),
|
||||
cc: z.string().optional().nullable(),
|
||||
bcc: z.string().optional().nullable(),
|
||||
replyToMessageId: z.string().optional().nullable(),
|
||||
@@ -72,7 +73,7 @@ export async function POST(request: NextRequest) {
|
||||
const message: any = {
|
||||
subject: validatedData.subject,
|
||||
body: {
|
||||
contentType: 'Text',
|
||||
contentType: validatedData.contentType || 'text',
|
||||
content: validatedData.body,
|
||||
},
|
||||
toRecipients,
|
||||
|
||||
@@ -170,7 +170,7 @@ async function processSecureActions(
|
||||
return { modifiedMessage, executedActions }
|
||||
}
|
||||
|
||||
// New helper function for direct login attempt
|
||||
// Helper function for direct login attempt
|
||||
async function attemptDirectLogin(
|
||||
stagehand: Stagehand,
|
||||
variables: Record<string, string> | undefined
|
||||
|
||||
@@ -202,7 +202,7 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise<
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error updating workspace:', error)
|
||||
logger.error('Error updating workspace:', error)
|
||||
return NextResponse.json({ error: 'Failed to update workspace' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,12 @@ import { WorkspaceInvitationEmail } from '@/components/emails/workspace-invitati
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { sendEmail } from '@/lib/email/mailer'
|
||||
import { getFromEmailAddress } from '@/lib/email/utils'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { hasWorkspaceAdminAccess } from '@/lib/permissions/utils'
|
||||
import { getBaseUrl } from '@/lib/urls/utils'
|
||||
|
||||
const logger = createLogger('WorkspaceInvitationAPI')
|
||||
|
||||
// GET /api/workspaces/invitations/[invitationId] - Get invitation details OR accept via token
|
||||
export async function GET(
|
||||
req: NextRequest,
|
||||
@@ -163,7 +166,7 @@ export async function GET(
|
||||
workspaceName: workspaceDetails.name,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error fetching workspace invitation:', error)
|
||||
logger.error('Error fetching workspace invitation:', error)
|
||||
return NextResponse.json({ error: 'Failed to fetch invitation details' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
@@ -211,7 +214,7 @@ export async function DELETE(
|
||||
|
||||
return NextResponse.json({ success: true })
|
||||
} catch (error) {
|
||||
console.error('Error deleting workspace invitation:', error)
|
||||
logger.error('Error deleting workspace invitation:', error)
|
||||
return NextResponse.json({ error: 'Failed to delete invitation' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
@@ -295,7 +298,7 @@ export async function POST(
|
||||
|
||||
return NextResponse.json({ success: true })
|
||||
} catch (error) {
|
||||
console.error('Error resending workspace invitation:', error)
|
||||
logger.error('Error resending workspace invitation:', error)
|
||||
return NextResponse.json({ error: 'Failed to resend invitation' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,11 @@ import { permissions, workspace } from '@sim/db/schema'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { hasWorkspaceAdminAccess } from '@/lib/permissions/utils'
|
||||
|
||||
const logger = createLogger('WorkspaceMemberAPI')
|
||||
|
||||
// DELETE /api/workspaces/members/[id] - Remove a member from a workspace
|
||||
export async function DELETE(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
||||
const { id: userId } = await params
|
||||
@@ -100,7 +103,7 @@ export async function DELETE(req: NextRequest, { params }: { params: Promise<{ i
|
||||
|
||||
return NextResponse.json({ success: true })
|
||||
} catch (error) {
|
||||
console.error('Error removing workspace member:', error)
|
||||
logger.error('Error removing workspace member:', error)
|
||||
return NextResponse.json({ error: 'Failed to remove workspace member' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,11 @@ import { permissions, type permissionTypeEnum, user } from '@sim/db/schema'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { hasAdminPermission } from '@/lib/permissions/utils'
|
||||
|
||||
const logger = createLogger('WorkspaceMemberAPI')
|
||||
|
||||
type PermissionType = (typeof permissionTypeEnum.enumValues)[number]
|
||||
|
||||
// Add a member to a workspace
|
||||
@@ -87,7 +90,7 @@ export async function POST(req: Request) {
|
||||
message: `User added to workspace with ${permission} permission`,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error adding workspace member:', error)
|
||||
logger.error('Error adding workspace member:', error)
|
||||
return NextResponse.json({ error: 'Failed to add workspace member' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ export async function POST(req: Request) {
|
||||
|
||||
return NextResponse.json({ workspace: newWorkspace })
|
||||
} catch (error) {
|
||||
console.error('Error creating workspace:', error)
|
||||
logger.error('Error creating workspace:', error)
|
||||
return NextResponse.json({ error: 'Failed to create workspace' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,10 @@ import { AlertCircle, Paperclip, Send, Square, X } from 'lucide-react'
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
import { VoiceInput } from '@/app/chat/components/input/voice-input'
|
||||
|
||||
const logger = createLogger('ChatInput')
|
||||
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
|
||||
const PLACEHOLDER_MOBILE = 'Enter a message'
|
||||
const PLACEHOLDER_DESKTOP = 'Enter a message or click the mic to speak'
|
||||
const MAX_TEXTAREA_HEIGHT = 120 // Max height in pixels (e.g., for about 3-4 lines)
|
||||
@@ -138,7 +142,7 @@ export const ChatInput: React.FC<{
|
||||
reader.readAsDataURL(file)
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error reading file:', error)
|
||||
logger.error('Error reading file:', error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,9 @@ import { useState } from 'react'
|
||||
import { Check, Copy, LibraryBig } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { useParams } from 'next/navigation'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
|
||||
const logger = createLogger('BaseOverviewComponent')
|
||||
|
||||
interface BaseOverviewProps {
|
||||
id?: string
|
||||
@@ -84,7 +87,7 @@ export function BaseOverview({
|
||||
setIsCopied(true)
|
||||
setTimeout(() => setIsCopied(false), 2000)
|
||||
} catch (err) {
|
||||
console.error('Failed to copy ID:', err)
|
||||
logger.error('Failed to copy ID:', err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -943,12 +943,10 @@ export function useKnowledgeUpload(options: UseKnowledgeUploadOptions = {}) {
|
||||
setUploadError(null)
|
||||
setUploadProgress({ stage: 'uploading', filesCompleted: 0, totalFiles: files.length })
|
||||
|
||||
// Upload files in batches with retry logic
|
||||
const uploadedFiles = await uploadFilesInBatches(files)
|
||||
|
||||
setUploadProgress((prev) => ({ ...prev, stage: 'processing' }))
|
||||
|
||||
// Start async document processing
|
||||
const processPayload = {
|
||||
documents: uploadedFiles.map((file) => ({
|
||||
...file,
|
||||
@@ -999,7 +997,6 @@ export function useKnowledgeUpload(options: UseKnowledgeUploadOptions = {}) {
|
||||
|
||||
const processResult = await processResponse.json()
|
||||
|
||||
// Validate process result structure
|
||||
if (!processResult.success) {
|
||||
throw new ProcessingError(
|
||||
`Document processing failed: ${processResult.error || 'Unknown error'}`,
|
||||
@@ -1018,7 +1015,6 @@ export function useKnowledgeUpload(options: UseKnowledgeUploadOptions = {}) {
|
||||
|
||||
logger.info(`Successfully started processing ${uploadedFiles.length} documents`)
|
||||
|
||||
// Call success callback
|
||||
options.onUploadComplete?.(uploadedFiles)
|
||||
|
||||
return uploadedFiles
|
||||
@@ -1029,8 +1025,7 @@ export function useKnowledgeUpload(options: UseKnowledgeUploadOptions = {}) {
|
||||
setUploadError(error)
|
||||
options.onError?.(error)
|
||||
|
||||
// Show user-friendly error message in console for debugging
|
||||
console.error('Document upload failed:', error.message)
|
||||
logger.error('Document upload failed:', error.message)
|
||||
|
||||
throw err
|
||||
} finally {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Loader2 } from 'lucide-react'
|
||||
import { useParams, useRouter, useSearchParams } from 'next/navigation'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { soehne } from '@/app/fonts/soehne/soehne'
|
||||
import Controls from '@/app/workspace/[workspaceId]/logs/components/dashboard/controls'
|
||||
import KPIs from '@/app/workspace/[workspaceId]/logs/components/dashboard/kpis'
|
||||
@@ -14,6 +15,8 @@ import { formatCost } from '@/providers/utils'
|
||||
import { useFilterStore } from '@/stores/logs/filters/store'
|
||||
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
||||
|
||||
const logger = createLogger('Dashboard')
|
||||
|
||||
type TimeFilter = '30m' | '1h' | '6h' | '12h' | '24h' | '3d' | '7d' | '14d' | '30d'
|
||||
|
||||
interface WorkflowExecution {
|
||||
@@ -362,7 +365,7 @@ export default function Dashboard() {
|
||||
})
|
||||
setGlobalLogsMeta({ offset: mappedLogs.length, hasMore: mappedLogs.length === 50 })
|
||||
} catch (err) {
|
||||
console.error('Error fetching executions:', err)
|
||||
logger.error('Error fetching executions:', err)
|
||||
setError(err instanceof Error ? err.message : 'An error occurred')
|
||||
} finally {
|
||||
setLoading(false)
|
||||
@@ -418,7 +421,7 @@ export default function Dashboard() {
|
||||
},
|
||||
}))
|
||||
} catch (err) {
|
||||
console.error('Error fetching workflow details:', err)
|
||||
logger.error('Error fetching workflow details:', err)
|
||||
}
|
||||
},
|
||||
[workspaceId, endTime, getStartTime, triggers]
|
||||
|
||||
@@ -115,7 +115,7 @@ export default async function TemplatePage({ params }: TemplatePageProps) {
|
||||
/>
|
||||
)
|
||||
} catch (error) {
|
||||
console.error('Error loading template:', error)
|
||||
logger.error('Error loading template:', error)
|
||||
return (
|
||||
<div className='flex h-screen items-center justify-center'>
|
||||
<div className='text-center'>
|
||||
|
||||
@@ -252,7 +252,7 @@ export const DiffControls = memo(function DiffControls() {
|
||||
logger.error('Failed to accept changes:', error)
|
||||
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'
|
||||
console.error('Workflow update failed:', errorMessage)
|
||||
logger.error('Workflow update failed:', errorMessage)
|
||||
alert(`Failed to save workflow changes: ${errorMessage}`)
|
||||
}
|
||||
}, [createCheckpoint, clearPreviewYaml, updatePreviewToolCallState, acceptChanges])
|
||||
|
||||
@@ -27,12 +27,15 @@ import { Label } from '@/components/ui/label'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { sanitizeForCopilot } from '@/lib/workflows/json-sanitizer'
|
||||
import { formatEditSequence } from '@/lib/workflows/training/compute-edit-sequence'
|
||||
import { useCurrentWorkflow } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-current-workflow'
|
||||
import { useCopilotTrainingStore } from '@/stores/copilot-training/store'
|
||||
|
||||
const logger = createLogger('TrainingModal')
|
||||
|
||||
/**
|
||||
* Modal for starting training sessions and viewing/exporting datasets
|
||||
*/
|
||||
@@ -136,7 +139,7 @@ export function TrainingModal() {
|
||||
|
||||
return result
|
||||
} catch (error) {
|
||||
console.error('Failed to send dataset to indexer:', error)
|
||||
logger.error('Failed to send dataset to indexer:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -325,7 +328,7 @@ export function TrainingModal() {
|
||||
setLiveWorkflowDescription('')
|
||||
setTimeout(() => setLiveWorkflowSent(false), 5000)
|
||||
} catch (error) {
|
||||
console.error('Failed to send live workflow:', error)
|
||||
logger.error('Failed to send live workflow:', error)
|
||||
setLiveWorkflowFailed(true)
|
||||
setTimeout(() => setLiveWorkflowFailed(false), 5000)
|
||||
} finally {
|
||||
|
||||
@@ -46,11 +46,21 @@ const SCOPE_DESCRIPTIONS: Record<string, string> = {
|
||||
'https://www.googleapis.com/auth/devstorage.read_only': 'Read files from Google Cloud Storage',
|
||||
'read:confluence-content.all': 'Read all Confluence content',
|
||||
'read:confluence-space.summary': 'Read Confluence space information',
|
||||
'read:space:confluence': 'View Confluence spaces',
|
||||
'read:space-details:confluence': 'View detailed Confluence space information',
|
||||
'write:confluence-content': 'Create and edit Confluence pages',
|
||||
'write:confluence-space': 'Manage Confluence spaces',
|
||||
'write:confluence-file': 'Upload files to Confluence',
|
||||
'write:comment:confluence': 'Create and manage comments',
|
||||
'write:attachment:confluence': 'Manage attachments',
|
||||
'read:page:confluence': 'View Confluence pages',
|
||||
'write:page:confluence': 'Create and update Confluence pages',
|
||||
'read:comment:confluence': 'View comments on Confluence pages',
|
||||
'write:comment:confluence': 'Create and update comments',
|
||||
'delete:comment:confluence': 'Delete comments from Confluence pages',
|
||||
'read:attachment:confluence': 'View attachments on Confluence pages',
|
||||
'write:attachment:confluence': 'Upload and manage attachments',
|
||||
'delete:attachment:confluence': 'Delete attachments from Confluence pages',
|
||||
'delete:page:confluence': 'Delete Confluence pages',
|
||||
'read:label:confluence': 'View labels on Confluence content',
|
||||
'write:label:confluence': 'Add and remove labels',
|
||||
'search:confluence': 'Search Confluence content',
|
||||
'readonly:content.attachment:confluence': 'View attachments',
|
||||
@@ -80,6 +90,10 @@ const SCOPE_DESCRIPTIONS: Record<string, string> = {
|
||||
'read:jira-user': 'Read your Jira user',
|
||||
'read:jira-work': 'Read your Jira work',
|
||||
'write:jira-work': 'Write to your Jira work',
|
||||
'manage:jira-webhook': 'Register and manage Jira webhooks',
|
||||
'read:webhook:jira': 'View Jira webhooks',
|
||||
'write:webhook:jira': 'Create and update Jira webhooks',
|
||||
'delete:webhook:jira': 'Delete Jira webhooks',
|
||||
'read:issue-event:jira': 'Read your Jira issue events',
|
||||
'write:issue:jira': 'Write to your Jira issues',
|
||||
'read:project:jira': 'Read your Jira projects',
|
||||
@@ -94,7 +108,10 @@ const SCOPE_DESCRIPTIONS: Record<string, string> = {
|
||||
'read:user:jira': 'Read your Jira user',
|
||||
'read:field-configuration:jira': 'Read your Jira field configuration',
|
||||
'read:issue-details:jira': 'Read your Jira issue details',
|
||||
// New scopes for expanded Jira operations
|
||||
'read:field:jira': 'Read Jira field configurations',
|
||||
'read:jql:jira': 'Use JQL to filter Jira issues',
|
||||
'read:comment.property:jira': 'Read Jira comment properties',
|
||||
'read:issue.property:jira': 'Read Jira issue properties',
|
||||
'delete:issue:jira': 'Delete Jira issues',
|
||||
'write:comment:jira': 'Add and update comments on Jira issues',
|
||||
'read:comment:jira': 'Read comments on Jira issues',
|
||||
|
||||
@@ -523,7 +523,7 @@ export function ToolInput({
|
||||
label: toolParams?.toolConfig?.name || toolId,
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error getting tool config for ${toolId}:`, error)
|
||||
logger.error(`Error getting tool config for ${toolId}:`, error)
|
||||
return {
|
||||
id: toolId,
|
||||
label: toolId,
|
||||
@@ -1012,11 +1012,13 @@ export function ToolInput({
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{uiComponent.options?.map((option: any) => (
|
||||
<SelectItem key={option.id} value={option.id}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
{uiComponent.options
|
||||
?.filter((option: any) => option.id !== '')
|
||||
.map((option: any) => (
|
||||
<SelectItem key={option.id} value={option.id}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)
|
||||
@@ -1624,11 +1626,13 @@ export function ToolInput({
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{operationOptions.map((option) => (
|
||||
<SelectItem key={option.id} value={option.id}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
{operationOptions
|
||||
.filter((option) => option.id !== '')
|
||||
.map((option) => (
|
||||
<SelectItem key={option.id} value={option.id}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
@@ -20,8 +20,6 @@ export async function applyWorkflowDiff(
|
||||
content: string,
|
||||
format: EditorFormat
|
||||
): Promise<ApplyResult> {
|
||||
console.log('🔥 applyWorkflowDiff called!', { format, contentLength: content.length })
|
||||
|
||||
try {
|
||||
const { activeWorkflowId } = useWorkflowRegistry.getState()
|
||||
|
||||
@@ -41,8 +39,6 @@ export async function applyWorkflowDiff(
|
||||
})
|
||||
|
||||
if (format === 'yaml') {
|
||||
console.log('🔥 Processing YAML format!')
|
||||
|
||||
logger.info('Processing YAML format - calling consolidated YAML endpoint')
|
||||
|
||||
try {
|
||||
@@ -79,9 +75,6 @@ export async function applyWorkflowDiff(
|
||||
warnings: result.warnings || [],
|
||||
})
|
||||
|
||||
// Auto layout is now handled automatically by the backend system
|
||||
// when applyAutoLayout is true in the request
|
||||
|
||||
// Calculate applied operations (blocks + edges)
|
||||
const appliedOperations = (result.data?.blocksCount || 0) + (result.data?.edgesCount || 0)
|
||||
|
||||
|
||||
@@ -3,8 +3,11 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useParams, useRouter } from 'next/navigation'
|
||||
import { LoadingAgent } from '@/components/ui/loading-agent'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
||||
|
||||
const logger = createLogger('WorkflowsPage')
|
||||
|
||||
export default function WorkflowsPage() {
|
||||
const router = useRouter()
|
||||
const { workflows, isLoading, loadWorkflows, setActiveWorkflow } = useWorkflowRegistry()
|
||||
@@ -20,7 +23,7 @@ export default function WorkflowsPage() {
|
||||
await loadWorkflows(workspaceId)
|
||||
setHasInitialized(true)
|
||||
} catch (error) {
|
||||
console.error('Failed to load workflows for workspace:', error)
|
||||
logger.error('Failed to load workflows for workspace:', error)
|
||||
setHasInitialized(true) // Still mark as initialized to show error state
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ export const AirtableBlock: BlockConfig<AirtableResponse> = {
|
||||
title: 'Max Records',
|
||||
type: 'short-input',
|
||||
layout: 'half',
|
||||
placeholder: 'Maximum records to return (optional)',
|
||||
placeholder: 'Maximum records to return',
|
||||
condition: { field: 'operation', value: 'list' },
|
||||
},
|
||||
{
|
||||
|
||||
@@ -19,7 +19,6 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Read Page', id: 'read' },
|
||||
{ label: 'Create Page', id: 'create' },
|
||||
@@ -32,9 +31,7 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
|
||||
{ label: 'Delete Comment', id: 'delete_comment' },
|
||||
{ label: 'List Attachments', id: 'list_attachments' },
|
||||
{ label: 'Delete Attachment', id: 'delete_attachment' },
|
||||
{ label: 'Add Label', id: 'add_label' },
|
||||
{ label: 'List Labels', id: 'list_labels' },
|
||||
{ label: 'Remove Label', id: 'remove_label' },
|
||||
{ label: 'Get Space', id: 'get_space' },
|
||||
{ label: 'List Spaces', id: 'list_spaces' },
|
||||
],
|
||||
@@ -44,7 +41,6 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
|
||||
id: 'domain',
|
||||
title: 'Domain',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter Confluence domain (e.g., simstudio.atlassian.net)',
|
||||
required: true,
|
||||
},
|
||||
@@ -52,20 +48,29 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
|
||||
id: 'credential',
|
||||
title: 'Confluence Account',
|
||||
type: 'oauth-input',
|
||||
layout: 'full',
|
||||
provider: 'confluence',
|
||||
serviceId: 'confluence',
|
||||
requiredScopes: [
|
||||
'read:confluence-content.all',
|
||||
'read:confluence-space.summary',
|
||||
'read:space:confluence',
|
||||
'read:space-details:confluence',
|
||||
'write:confluence-content',
|
||||
'write:confluence-space',
|
||||
'write:confluence-file',
|
||||
'read:content:confluence',
|
||||
'read:page:confluence',
|
||||
'write:page:confluence',
|
||||
'read:comment:confluence',
|
||||
'write:comment:confluence',
|
||||
'delete:comment:confluence',
|
||||
'read:attachment:confluence',
|
||||
'write:attachment:confluence',
|
||||
'delete:attachment:confluence',
|
||||
'delete:page:confluence',
|
||||
'read:label:confluence',
|
||||
'write:label:confluence',
|
||||
'search:confluence',
|
||||
'readonly:content.attachment:confluence',
|
||||
'read:me',
|
||||
'offline_access',
|
||||
],
|
||||
@@ -76,7 +81,6 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
|
||||
id: 'pageId',
|
||||
title: 'Select Page',
|
||||
type: 'file-selector',
|
||||
layout: 'full',
|
||||
canonicalParamId: 'pageId',
|
||||
provider: 'confluence',
|
||||
serviceId: 'confluence',
|
||||
@@ -88,7 +92,6 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
|
||||
id: 'manualPageId',
|
||||
title: 'Page ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
canonicalParamId: 'pageId',
|
||||
placeholder: 'Enter Confluence page ID',
|
||||
mode: 'advanced',
|
||||
@@ -97,15 +100,14 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
|
||||
id: 'spaceId',
|
||||
title: 'Space ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter Confluence space ID',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: ['create', 'get_space'] },
|
||||
},
|
||||
{
|
||||
id: 'title',
|
||||
title: 'Title',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter title for the page',
|
||||
condition: { field: 'operation', value: ['create', 'update'] },
|
||||
},
|
||||
@@ -113,7 +115,6 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
|
||||
id: 'content',
|
||||
title: 'Content',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter content for the page',
|
||||
condition: { field: 'operation', value: ['create', 'update'] },
|
||||
},
|
||||
@@ -121,7 +122,6 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
|
||||
id: 'parentId',
|
||||
title: 'Parent Page ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter parent page ID (optional)',
|
||||
condition: { field: 'operation', value: 'create' },
|
||||
},
|
||||
@@ -129,7 +129,6 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
|
||||
id: 'query',
|
||||
title: 'Search Query',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter search query',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'search' },
|
||||
@@ -138,7 +137,6 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
|
||||
id: 'comment',
|
||||
title: 'Comment Text',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter comment text',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: ['create_comment', 'update_comment'] },
|
||||
@@ -147,7 +145,6 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
|
||||
id: 'commentId',
|
||||
title: 'Comment ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter comment ID',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: ['update_comment', 'delete_comment'] },
|
||||
@@ -156,7 +153,6 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
|
||||
id: 'attachmentId',
|
||||
title: 'Attachment ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter attachment ID',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'delete_attachment' },
|
||||
@@ -165,7 +161,6 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
|
||||
id: 'labelName',
|
||||
title: 'Label Name',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter label name',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: ['add_label', 'remove_label'] },
|
||||
@@ -174,7 +169,6 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
|
||||
id: 'limit',
|
||||
title: 'Limit',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter maximum number of results (default: 25)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -195,9 +189,7 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
|
||||
'confluence_delete_comment',
|
||||
'confluence_list_attachments',
|
||||
'confluence_delete_attachment',
|
||||
'confluence_add_label',
|
||||
'confluence_list_labels',
|
||||
'confluence_remove_label',
|
||||
'confluence_get_space',
|
||||
'confluence_list_spaces',
|
||||
],
|
||||
@@ -226,12 +218,8 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
|
||||
return 'confluence_list_attachments'
|
||||
case 'delete_attachment':
|
||||
return 'confluence_delete_attachment'
|
||||
case 'add_label':
|
||||
return 'confluence_add_label'
|
||||
case 'list_labels':
|
||||
return 'confluence_list_labels'
|
||||
case 'remove_label':
|
||||
return 'confluence_remove_label'
|
||||
case 'get_space':
|
||||
return 'confluence_get_space'
|
||||
case 'list_spaces':
|
||||
@@ -253,9 +241,7 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
|
||||
'create_comment',
|
||||
'list_comments',
|
||||
'list_attachments',
|
||||
'add_label',
|
||||
'list_labels',
|
||||
'remove_label',
|
||||
]
|
||||
|
||||
// Operations that require spaceId
|
||||
|
||||
@@ -18,7 +18,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Send Message', id: 'discord_send_message' },
|
||||
{ label: 'Get Channel Messages', id: 'discord_get_messages' },
|
||||
@@ -62,7 +61,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'botToken',
|
||||
title: 'Bot Token',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter Discord bot token',
|
||||
password: true,
|
||||
required: true,
|
||||
@@ -71,7 +69,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'serverId',
|
||||
title: 'Server ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter Discord server ID',
|
||||
required: true,
|
||||
provider: 'discord',
|
||||
@@ -82,7 +79,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'channelId',
|
||||
title: 'Channel ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter Discord channel ID',
|
||||
required: true,
|
||||
provider: 'discord',
|
||||
@@ -112,7 +108,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'messageId',
|
||||
title: 'Message ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter message ID',
|
||||
required: true,
|
||||
condition: {
|
||||
@@ -133,7 +128,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'content',
|
||||
title: 'Message Content',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter message content...',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -145,7 +139,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'emoji',
|
||||
title: 'Emoji',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter emoji (e.g., 👍 or custom:123456789)',
|
||||
required: true,
|
||||
condition: {
|
||||
@@ -158,7 +151,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'userId',
|
||||
title: 'User ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter Discord user ID',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -180,7 +172,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'threadId',
|
||||
title: 'Thread ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter thread ID',
|
||||
required: true,
|
||||
condition: {
|
||||
@@ -193,7 +184,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'name',
|
||||
title: 'Name',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter name',
|
||||
required: true,
|
||||
condition: {
|
||||
@@ -201,19 +191,27 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
value: [
|
||||
'discord_create_thread',
|
||||
'discord_create_channel',
|
||||
'discord_update_channel',
|
||||
'discord_create_role',
|
||||
'discord_update_role',
|
||||
'discord_create_webhook',
|
||||
],
|
||||
},
|
||||
},
|
||||
// Name (optional for updates)
|
||||
{
|
||||
id: 'name',
|
||||
title: 'Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter new name (optional)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['discord_update_channel', 'discord_update_role'],
|
||||
},
|
||||
},
|
||||
// Role ID
|
||||
{
|
||||
id: 'roleId',
|
||||
title: 'Role ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter role ID',
|
||||
required: true,
|
||||
condition: {
|
||||
@@ -231,7 +229,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'webhookId',
|
||||
title: 'Webhook ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter webhook ID',
|
||||
required: true,
|
||||
condition: {
|
||||
@@ -244,7 +241,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'webhookToken',
|
||||
title: 'Webhook Token',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter webhook token',
|
||||
required: true,
|
||||
condition: {
|
||||
@@ -257,7 +253,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'inviteCode',
|
||||
title: 'Invite Code',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter invite code',
|
||||
required: true,
|
||||
condition: {
|
||||
@@ -270,7 +265,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'archived',
|
||||
title: 'Archived',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Archive', id: 'true' },
|
||||
{ label: 'Unarchive', id: 'false' },
|
||||
@@ -286,7 +280,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'topic',
|
||||
title: 'Topic',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter channel topic (optional)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -298,7 +291,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'color',
|
||||
title: 'Color',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter color as integer (e.g., 16711680 for red)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -310,7 +302,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'nick',
|
||||
title: 'Nickname',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter new nickname',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -322,7 +313,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'reason',
|
||||
title: 'Reason',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter reason for this action',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -334,7 +324,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'limit',
|
||||
title: 'Message Limit',
|
||||
type: 'short-input',
|
||||
layout: 'half',
|
||||
placeholder: 'Number of messages (default: 10, max: 100)',
|
||||
condition: { field: 'operation', value: 'discord_get_messages' },
|
||||
},
|
||||
@@ -343,7 +332,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'autoArchiveDuration',
|
||||
title: 'Auto Archive Duration (minutes)',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: '1 hour (60 minutes)', id: '60' },
|
||||
{ label: '24 hours (1440 minutes)', id: '1440' },
|
||||
@@ -361,7 +349,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'channelType',
|
||||
title: 'Channel Type',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Text Channel', id: '0' },
|
||||
{ label: 'Voice Channel', id: '2' },
|
||||
@@ -380,7 +367,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'parentId',
|
||||
title: 'Parent Category ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter parent category ID (optional)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -392,7 +378,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'hoist',
|
||||
title: 'Display Separately',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Yes - Display role members separately', id: 'true' },
|
||||
{ label: "No - Don't display separately", id: 'false' },
|
||||
@@ -408,7 +393,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'mentionable',
|
||||
title: 'Mentionable',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Yes - Role can be mentioned', id: 'true' },
|
||||
{ label: 'No - Role cannot be mentioned', id: 'false' },
|
||||
@@ -424,7 +408,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'deleteMessageDays',
|
||||
title: 'Delete Message History',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: "Don't delete any messages", id: '0' },
|
||||
{ label: 'Delete messages from last 1 day', id: '1' },
|
||||
@@ -441,7 +424,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'mute',
|
||||
title: 'Server Mute',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Mute member', id: 'true' },
|
||||
{ label: 'Unmute member', id: 'false' },
|
||||
@@ -456,7 +438,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'deaf',
|
||||
title: 'Server Deafen',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Deafen member', id: 'true' },
|
||||
{ label: 'Undeafen member', id: 'false' },
|
||||
@@ -471,7 +452,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'maxAge',
|
||||
title: 'Invite Expiration',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Never expire', id: '0' },
|
||||
{ label: '30 minutes', id: '1800' },
|
||||
@@ -492,7 +472,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'maxUses',
|
||||
title: 'Max Uses',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Maximum number of uses (0 = unlimited)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -504,7 +483,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'temporary',
|
||||
title: 'Temporary Membership',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'No - Grant permanent membership', id: 'false' },
|
||||
{ label: 'Yes - Kick on disconnect if no role', id: 'true' },
|
||||
@@ -520,7 +498,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'username',
|
||||
title: 'Override Username',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Custom username to display (optional)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -532,7 +509,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'attachmentFiles',
|
||||
title: 'Attachments',
|
||||
type: 'file-upload',
|
||||
layout: 'full',
|
||||
canonicalParamId: 'files',
|
||||
placeholder: 'Upload files to attach',
|
||||
condition: { field: 'operation', value: 'discord_send_message' },
|
||||
@@ -544,7 +520,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
id: 'files',
|
||||
title: 'File Attachments',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
canonicalParamId: 'files',
|
||||
placeholder: 'Reference files from previous blocks',
|
||||
condition: { field: 'operation', value: 'discord_send_message' },
|
||||
|
||||
@@ -19,7 +19,6 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Search', id: 'exa_search' },
|
||||
{ label: 'Get Contents', id: 'exa_get_contents' },
|
||||
@@ -34,7 +33,6 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
|
||||
id: 'query',
|
||||
title: 'Search Query',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter your search query...',
|
||||
condition: { field: 'operation', value: 'exa_search' },
|
||||
required: true,
|
||||
@@ -43,7 +41,6 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
|
||||
id: 'numResults',
|
||||
title: 'Number of Results',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '10',
|
||||
condition: { field: 'operation', value: 'exa_search' },
|
||||
},
|
||||
@@ -51,14 +48,12 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
|
||||
id: 'useAutoprompt',
|
||||
title: 'Use Autoprompt',
|
||||
type: 'switch',
|
||||
layout: 'full',
|
||||
condition: { field: 'operation', value: 'exa_search' },
|
||||
},
|
||||
{
|
||||
id: 'type',
|
||||
title: 'Search Type',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Auto', id: 'auto' },
|
||||
{ label: 'Neural', id: 'neural' },
|
||||
@@ -72,7 +67,6 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
|
||||
id: 'includeDomains',
|
||||
title: 'Include Domains',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'example.com, another.com (comma-separated)',
|
||||
condition: { field: 'operation', value: 'exa_search' },
|
||||
},
|
||||
@@ -80,47 +74,13 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
|
||||
id: 'excludeDomains',
|
||||
title: 'Exclude Domains',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'exclude.com, another.com (comma-separated)',
|
||||
condition: { field: 'operation', value: 'exa_search' },
|
||||
},
|
||||
{
|
||||
id: 'startPublishedDate',
|
||||
title: 'Start Published Date',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '2024-01-01',
|
||||
condition: { field: 'operation', value: 'exa_search' },
|
||||
},
|
||||
{
|
||||
id: 'endPublishedDate',
|
||||
title: 'End Published Date',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '2024-12-31',
|
||||
condition: { field: 'operation', value: 'exa_search' },
|
||||
},
|
||||
{
|
||||
id: 'startCrawlDate',
|
||||
title: 'Start Crawl Date',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '2024-01-01',
|
||||
condition: { field: 'operation', value: 'exa_search' },
|
||||
},
|
||||
{
|
||||
id: 'endCrawlDate',
|
||||
title: 'End Crawl Date',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '2024-12-31',
|
||||
condition: { field: 'operation', value: 'exa_search' },
|
||||
},
|
||||
{
|
||||
id: 'category',
|
||||
title: 'Category Filter',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'None', id: '' },
|
||||
{ label: 'Company', id: 'company' },
|
||||
@@ -140,28 +100,24 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
|
||||
id: 'text',
|
||||
title: 'Include Text',
|
||||
type: 'switch',
|
||||
layout: 'full',
|
||||
condition: { field: 'operation', value: 'exa_search' },
|
||||
},
|
||||
{
|
||||
id: 'highlights',
|
||||
title: 'Include Highlights',
|
||||
type: 'switch',
|
||||
layout: 'full',
|
||||
condition: { field: 'operation', value: 'exa_search' },
|
||||
},
|
||||
{
|
||||
id: 'summary',
|
||||
title: 'Include Summary',
|
||||
type: 'switch',
|
||||
layout: 'full',
|
||||
condition: { field: 'operation', value: 'exa_search' },
|
||||
},
|
||||
{
|
||||
id: 'livecrawl',
|
||||
title: 'Live Crawl Mode',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Never (default)', id: 'never' },
|
||||
{ label: 'Fallback', id: 'fallback' },
|
||||
@@ -175,7 +131,6 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
|
||||
id: 'urls',
|
||||
title: 'URLs',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter URLs to retrieve content from (comma-separated)...',
|
||||
condition: { field: 'operation', value: 'exa_get_contents' },
|
||||
required: true,
|
||||
@@ -184,14 +139,12 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
|
||||
id: 'text',
|
||||
title: 'Include Text',
|
||||
type: 'switch',
|
||||
layout: 'full',
|
||||
condition: { field: 'operation', value: 'exa_get_contents' },
|
||||
},
|
||||
{
|
||||
id: 'summaryQuery',
|
||||
title: 'Summary Query',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter a query to guide the summary generation...',
|
||||
condition: { field: 'operation', value: 'exa_get_contents' },
|
||||
},
|
||||
@@ -199,7 +152,6 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
|
||||
id: 'subpages',
|
||||
title: 'Number of Subpages',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '5',
|
||||
condition: { field: 'operation', value: 'exa_get_contents' },
|
||||
},
|
||||
@@ -207,7 +159,6 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
|
||||
id: 'subpageTarget',
|
||||
title: 'Subpage Target Keywords',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'docs, tutorial, about (comma-separated)',
|
||||
condition: { field: 'operation', value: 'exa_get_contents' },
|
||||
},
|
||||
@@ -215,7 +166,6 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
|
||||
id: 'highlights',
|
||||
title: 'Include Highlights',
|
||||
type: 'switch',
|
||||
layout: 'full',
|
||||
condition: { field: 'operation', value: 'exa_get_contents' },
|
||||
},
|
||||
// Find Similar Links operation inputs
|
||||
@@ -223,7 +173,6 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
|
||||
id: 'url',
|
||||
title: 'URL',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter URL to find similar links for...',
|
||||
condition: { field: 'operation', value: 'exa_find_similar_links' },
|
||||
required: true,
|
||||
@@ -232,7 +181,6 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
|
||||
id: 'numResults',
|
||||
title: 'Number of Results',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '10',
|
||||
condition: { field: 'operation', value: 'exa_find_similar_links' },
|
||||
},
|
||||
@@ -240,14 +188,12 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
|
||||
id: 'text',
|
||||
title: 'Include Text',
|
||||
type: 'switch',
|
||||
layout: 'full',
|
||||
condition: { field: 'operation', value: 'exa_find_similar_links' },
|
||||
},
|
||||
{
|
||||
id: 'includeDomains',
|
||||
title: 'Include Domains',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'example.com, another.com (comma-separated)',
|
||||
condition: { field: 'operation', value: 'exa_find_similar_links' },
|
||||
},
|
||||
@@ -255,7 +201,6 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
|
||||
id: 'excludeDomains',
|
||||
title: 'Exclude Domains',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'exclude.com, another.com (comma-separated)',
|
||||
condition: { field: 'operation', value: 'exa_find_similar_links' },
|
||||
},
|
||||
@@ -263,46 +208,12 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
|
||||
id: 'excludeSourceDomain',
|
||||
title: 'Exclude Source Domain',
|
||||
type: 'switch',
|
||||
layout: 'full',
|
||||
condition: { field: 'operation', value: 'exa_find_similar_links' },
|
||||
},
|
||||
{
|
||||
id: 'startPublishedDate',
|
||||
title: 'Start Published Date',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '2024-01-01',
|
||||
condition: { field: 'operation', value: 'exa_find_similar_links' },
|
||||
},
|
||||
{
|
||||
id: 'endPublishedDate',
|
||||
title: 'End Published Date',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '2024-12-31',
|
||||
condition: { field: 'operation', value: 'exa_find_similar_links' },
|
||||
},
|
||||
{
|
||||
id: 'startCrawlDate',
|
||||
title: 'Start Crawl Date',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '2024-01-01',
|
||||
condition: { field: 'operation', value: 'exa_find_similar_links' },
|
||||
},
|
||||
{
|
||||
id: 'endCrawlDate',
|
||||
title: 'End Crawl Date',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '2024-12-31',
|
||||
condition: { field: 'operation', value: 'exa_find_similar_links' },
|
||||
},
|
||||
{
|
||||
id: 'category',
|
||||
title: 'Category Filter',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'None', id: '' },
|
||||
{ label: 'Company', id: 'company' },
|
||||
@@ -322,21 +233,18 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
|
||||
id: 'highlights',
|
||||
title: 'Include Highlights',
|
||||
type: 'switch',
|
||||
layout: 'full',
|
||||
condition: { field: 'operation', value: 'exa_find_similar_links' },
|
||||
},
|
||||
{
|
||||
id: 'summary',
|
||||
title: 'Include Summary',
|
||||
type: 'switch',
|
||||
layout: 'full',
|
||||
condition: { field: 'operation', value: 'exa_find_similar_links' },
|
||||
},
|
||||
{
|
||||
id: 'livecrawl',
|
||||
title: 'Live Crawl Mode',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Never (default)', id: 'never' },
|
||||
{ label: 'Fallback', id: 'fallback' },
|
||||
@@ -350,7 +258,6 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
|
||||
id: 'query',
|
||||
title: 'Question',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter your question...',
|
||||
condition: { field: 'operation', value: 'exa_answer' },
|
||||
required: true,
|
||||
@@ -359,7 +266,6 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
|
||||
id: 'text',
|
||||
title: 'Include Text',
|
||||
type: 'switch',
|
||||
layout: 'full',
|
||||
condition: { field: 'operation', value: 'exa_answer' },
|
||||
},
|
||||
// Research operation inputs
|
||||
@@ -367,7 +273,6 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
|
||||
id: 'query',
|
||||
title: 'Research Query',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter your research topic or question...',
|
||||
condition: { field: 'operation', value: 'exa_research' },
|
||||
required: true,
|
||||
@@ -376,7 +281,6 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
|
||||
id: 'model',
|
||||
title: 'Research Model',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Standard (default)', id: 'exa-research' },
|
||||
{ label: 'Fast', id: 'exa-research-fast' },
|
||||
@@ -390,7 +294,6 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
|
||||
id: 'apiKey',
|
||||
title: 'API Key',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter your Exa API key',
|
||||
password: true,
|
||||
required: true,
|
||||
@@ -443,10 +346,6 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
|
||||
type: { type: 'string', description: 'Search type' },
|
||||
includeDomains: { type: 'string', description: 'Include domains filter' },
|
||||
excludeDomains: { type: 'string', description: 'Exclude domains filter' },
|
||||
startPublishedDate: { type: 'string', description: 'Start published date filter' },
|
||||
endPublishedDate: { type: 'string', description: 'End published date filter' },
|
||||
startCrawlDate: { type: 'string', description: 'Start crawl date filter' },
|
||||
endCrawlDate: { type: 'string', description: 'End crawl date filter' },
|
||||
category: { type: 'string', description: 'Category filter' },
|
||||
text: { type: 'boolean', description: 'Include text content' },
|
||||
highlights: { type: 'boolean', description: 'Include highlights' },
|
||||
|
||||
@@ -9,7 +9,7 @@ export const FirecrawlBlock: BlockConfig<FirecrawlResponse> = {
|
||||
description: 'Scrape, search, crawl, map, and extract web data',
|
||||
authMode: AuthMode.ApiKey,
|
||||
longDescription:
|
||||
'Integrate Firecrawl into the workflow. Can scrape pages, search the web, crawl entire websites, map URL structures, and extract structured data using AI.',
|
||||
'Integrate Firecrawl into the workflow. Scrape pages, search the web, crawl entire sites, map URL structures, and extract structured data with AI.',
|
||||
docsLink: 'https://docs.sim.ai/tools/firecrawl',
|
||||
category: 'tools',
|
||||
bgColor: '#181C1E',
|
||||
@@ -19,7 +19,6 @@ export const FirecrawlBlock: BlockConfig<FirecrawlResponse> = {
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Scrape', id: 'scrape' },
|
||||
{ label: 'Search', id: 'search' },
|
||||
@@ -33,7 +32,6 @@ export const FirecrawlBlock: BlockConfig<FirecrawlResponse> = {
|
||||
id: 'url',
|
||||
title: 'Website URL',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter the website URL',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -45,44 +43,19 @@ export const FirecrawlBlock: BlockConfig<FirecrawlResponse> = {
|
||||
id: 'urls',
|
||||
title: 'URLs',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder:
|
||||
'Enter URLs as JSON array (e.g., ["https://example.com", "https://example.com/about"])',
|
||||
placeholder: '["https://example.com/page1", "https://example.com/page2"]',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'extract',
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'query',
|
||||
title: 'Search Query',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter the search query',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'search',
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'prompt',
|
||||
title: 'Prompt',
|
||||
title: 'Extraction Prompt',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Natural language instruction for extraction or crawling',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['extract', 'crawl'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'schema',
|
||||
title: 'Schema',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'JSON Schema for data extraction',
|
||||
placeholder:
|
||||
'Describe what data to extract (e.g., "Extract product names, prices, and descriptions")',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'extract',
|
||||
@@ -92,7 +65,6 @@ export const FirecrawlBlock: BlockConfig<FirecrawlResponse> = {
|
||||
id: 'onlyMainContent',
|
||||
title: 'Only Main Content',
|
||||
type: 'switch',
|
||||
layout: 'half',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'scrape',
|
||||
@@ -102,60 +74,16 @@ export const FirecrawlBlock: BlockConfig<FirecrawlResponse> = {
|
||||
id: 'formats',
|
||||
title: 'Output Formats',
|
||||
type: 'long-input',
|
||||
layout: 'half',
|
||||
placeholder: '["markdown", "html"]',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'scrape',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'limit',
|
||||
title: 'Limit',
|
||||
type: 'short-input',
|
||||
layout: 'half',
|
||||
placeholder: '100',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['crawl', 'search', 'map'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'timeout',
|
||||
title: 'Timeout (ms)',
|
||||
type: 'short-input',
|
||||
layout: 'half',
|
||||
placeholder: '60000',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['scrape', 'search', 'map'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'mobile',
|
||||
title: 'Mobile Mode',
|
||||
type: 'switch',
|
||||
layout: 'half',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'scrape',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'blockAds',
|
||||
title: 'Block Ads',
|
||||
type: 'switch',
|
||||
layout: 'half',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'scrape',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'waitFor',
|
||||
title: 'Wait For (ms)',
|
||||
type: 'short-input',
|
||||
layout: 'half',
|
||||
placeholder: '0',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -163,83 +91,49 @@ export const FirecrawlBlock: BlockConfig<FirecrawlResponse> = {
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'excludePaths',
|
||||
title: 'Exclude Paths',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: '["^/admin", "^/private"]',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'crawl',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'includePaths',
|
||||
title: 'Include Paths',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: '["^/blog", "^/docs"]',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'crawl',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'allowSubdomains',
|
||||
title: 'Allow Subdomains',
|
||||
id: 'mobile',
|
||||
title: 'Mobile Mode',
|
||||
type: 'switch',
|
||||
layout: 'half',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'crawl',
|
||||
value: 'scrape',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'allowExternalLinks',
|
||||
title: 'Allow External Links',
|
||||
type: 'switch',
|
||||
layout: 'half',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'crawl',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'search',
|
||||
title: 'Search Filter',
|
||||
id: 'timeout',
|
||||
title: 'Timeout (ms)',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Filter results by relevance (e.g., "blog")',
|
||||
placeholder: '60000',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'map',
|
||||
value: ['scrape', 'search'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'includeSubdomains',
|
||||
title: 'Include Subdomains',
|
||||
type: 'switch',
|
||||
layout: 'half',
|
||||
id: 'limit',
|
||||
title: 'Limit',
|
||||
type: 'short-input',
|
||||
placeholder: '100',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['map', 'extract'],
|
||||
value: ['crawl', 'map', 'search'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'showSources',
|
||||
title: 'Show Sources',
|
||||
type: 'switch',
|
||||
layout: 'half',
|
||||
id: 'query',
|
||||
title: 'Search Query',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter the search query',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'extract',
|
||||
value: 'search',
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'apiKey',
|
||||
title: 'API Key',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter your Firecrawl API key',
|
||||
password: true,
|
||||
required: true,
|
||||
@@ -271,49 +165,70 @@ export const FirecrawlBlock: BlockConfig<FirecrawlResponse> = {
|
||||
}
|
||||
},
|
||||
params: (params) => {
|
||||
const { operation, limit, urls, formats, schema, ...rest } = params
|
||||
const {
|
||||
operation,
|
||||
limit,
|
||||
urls,
|
||||
formats,
|
||||
timeout,
|
||||
waitFor,
|
||||
url,
|
||||
query,
|
||||
onlyMainContent,
|
||||
mobile,
|
||||
prompt,
|
||||
apiKey,
|
||||
} = params
|
||||
|
||||
// Parse JSON fields if provided as strings
|
||||
const parsedParams: Record<string, any> = { ...rest }
|
||||
const result: Record<string, any> = { apiKey }
|
||||
|
||||
// Handle limit as number
|
||||
if (limit) {
|
||||
parsedParams.limit = Number.parseInt(limit)
|
||||
// Handle operation-specific fields
|
||||
switch (operation) {
|
||||
case 'scrape':
|
||||
if (url) result.url = url
|
||||
if (formats) {
|
||||
try {
|
||||
result.formats = typeof formats === 'string' ? JSON.parse(formats) : formats
|
||||
} catch {
|
||||
result.formats = ['markdown']
|
||||
}
|
||||
}
|
||||
if (timeout) result.timeout = Number.parseInt(timeout)
|
||||
if (waitFor) result.waitFor = Number.parseInt(waitFor)
|
||||
if (onlyMainContent != null) result.onlyMainContent = onlyMainContent
|
||||
if (mobile != null) result.mobile = mobile
|
||||
break
|
||||
|
||||
case 'search':
|
||||
if (query) result.query = query
|
||||
if (timeout) result.timeout = Number.parseInt(timeout)
|
||||
if (limit) result.limit = Number.parseInt(limit)
|
||||
break
|
||||
|
||||
case 'crawl':
|
||||
if (url) result.url = url
|
||||
if (limit) result.limit = Number.parseInt(limit)
|
||||
if (onlyMainContent != null) result.onlyMainContent = onlyMainContent
|
||||
break
|
||||
|
||||
case 'map':
|
||||
if (url) result.url = url
|
||||
if (limit) result.limit = Number.parseInt(limit)
|
||||
break
|
||||
|
||||
case 'extract':
|
||||
if (urls) {
|
||||
try {
|
||||
result.urls = typeof urls === 'string' ? JSON.parse(urls) : urls
|
||||
} catch {
|
||||
result.urls = [urls]
|
||||
}
|
||||
}
|
||||
if (prompt) result.prompt = prompt
|
||||
break
|
||||
}
|
||||
|
||||
// Handle JSON array fields
|
||||
if (urls && typeof urls === 'string') {
|
||||
try {
|
||||
parsedParams.urls = JSON.parse(urls)
|
||||
} catch {
|
||||
parsedParams.urls = [urls]
|
||||
}
|
||||
} else if (urls) {
|
||||
parsedParams.urls = urls
|
||||
}
|
||||
|
||||
if (formats && typeof formats === 'string') {
|
||||
try {
|
||||
parsedParams.formats = JSON.parse(formats)
|
||||
} catch {
|
||||
parsedParams.formats = ['markdown']
|
||||
}
|
||||
} else if (formats) {
|
||||
parsedParams.formats = formats
|
||||
}
|
||||
|
||||
if (schema && typeof schema === 'string') {
|
||||
try {
|
||||
parsedParams.schema = JSON.parse(schema)
|
||||
} catch {
|
||||
// Keep as string if not valid JSON
|
||||
parsedParams.schema = schema
|
||||
}
|
||||
} else if (schema) {
|
||||
parsedParams.schema = schema
|
||||
}
|
||||
|
||||
return parsedParams
|
||||
return result
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -323,40 +238,31 @@ export const FirecrawlBlock: BlockConfig<FirecrawlResponse> = {
|
||||
url: { type: 'string', description: 'Target website URL' },
|
||||
urls: { type: 'json', description: 'Array of URLs for extraction' },
|
||||
query: { type: 'string', description: 'Search query terms' },
|
||||
prompt: { type: 'string', description: 'Natural language instruction' },
|
||||
schema: { type: 'json', description: 'JSON Schema for extraction' },
|
||||
prompt: { type: 'string', description: 'Extraction prompt' },
|
||||
limit: { type: 'string', description: 'Result/page limit' },
|
||||
formats: { type: 'json', description: 'Output formats array' },
|
||||
onlyMainContent: { type: 'boolean', description: 'Extract only main content' },
|
||||
timeout: { type: 'number', description: 'Request timeout in ms' },
|
||||
waitFor: { type: 'number', description: 'Wait time before scraping in ms' },
|
||||
mobile: { type: 'boolean', description: 'Use mobile emulation' },
|
||||
blockAds: { type: 'boolean', description: 'Block ads and popups' },
|
||||
waitFor: { type: 'number', description: 'Wait time before scraping' },
|
||||
excludePaths: { type: 'json', description: 'Paths to exclude from crawl' },
|
||||
includePaths: { type: 'json', description: 'Paths to include in crawl' },
|
||||
allowSubdomains: { type: 'boolean', description: 'Allow subdomain crawling' },
|
||||
allowExternalLinks: { type: 'boolean', description: 'Allow external links' },
|
||||
search: { type: 'string', description: 'Search filter for map' },
|
||||
includeSubdomains: { type: 'boolean', description: 'Include subdomains' },
|
||||
showSources: { type: 'boolean', description: 'Show data sources' },
|
||||
onlyMainContent: { type: 'boolean', description: 'Extract only main content' },
|
||||
scrapeOptions: { type: 'json', description: 'Advanced scraping options' },
|
||||
},
|
||||
outputs: {
|
||||
// Scrape outputs
|
||||
// Scrape output
|
||||
markdown: { type: 'string', description: 'Page content markdown' },
|
||||
html: { type: 'string', description: 'Raw HTML content' },
|
||||
metadata: { type: 'json', description: 'Page metadata' },
|
||||
// Search outputs
|
||||
// Search output
|
||||
data: { type: 'json', description: 'Search results or extracted data' },
|
||||
warning: { type: 'string', description: 'Warning messages' },
|
||||
// Crawl outputs
|
||||
// Crawl output
|
||||
pages: { type: 'json', description: 'Crawled pages data' },
|
||||
total: { type: 'number', description: 'Total pages found' },
|
||||
creditsUsed: { type: 'number', description: 'Credits consumed' },
|
||||
// Map outputs
|
||||
// Map output
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
links: { type: 'json', description: 'Array of discovered URLs' },
|
||||
// Extract outputs
|
||||
links: { type: 'json', description: 'Discovered URLs array' },
|
||||
// Extract output
|
||||
sources: { type: 'json', description: 'Data sources array' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -84,6 +84,19 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
|
||||
condition: { field: 'operation', value: ['send_gmail', 'draft_gmail'] },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'contentType',
|
||||
title: 'Content Type',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Plain Text', id: 'text' },
|
||||
{ label: 'HTML', id: 'html' },
|
||||
],
|
||||
condition: { field: 'operation', value: ['send_gmail', 'draft_gmail'] },
|
||||
value: () => 'text',
|
||||
required: false,
|
||||
},
|
||||
// File upload (basic mode)
|
||||
{
|
||||
id: 'attachmentFiles',
|
||||
@@ -271,7 +284,7 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
|
||||
// Source label selector (basic mode)
|
||||
{
|
||||
id: 'sourceLabel',
|
||||
title: 'Remove From Label (Optional)',
|
||||
title: 'Remove From Label',
|
||||
type: 'folder-selector',
|
||||
layout: 'full',
|
||||
canonicalParamId: 'removeLabelIds',
|
||||
@@ -290,7 +303,7 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
|
||||
// Manual source label input (advanced mode)
|
||||
{
|
||||
id: 'manualSourceLabel',
|
||||
title: 'Remove From Label (Optional)',
|
||||
title: 'Remove From Label',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
canonicalParamId: 'removeLabelIds',
|
||||
@@ -482,6 +495,7 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
|
||||
to: { type: 'string', description: 'Recipient email address' },
|
||||
subject: { type: 'string', description: 'Email subject' },
|
||||
body: { type: 'string', description: 'Email content' },
|
||||
contentType: { type: 'string', description: 'Content type (text or html)' },
|
||||
threadId: { type: 'string', description: 'Thread ID to reply to (for threading)' },
|
||||
replyToMessageId: {
|
||||
type: 'string',
|
||||
|
||||
@@ -84,7 +84,7 @@ export const GoogleVaultBlock: BlockConfig = {
|
||||
},
|
||||
{
|
||||
id: 'fileName',
|
||||
title: 'File Name (optional)',
|
||||
title: 'File Name',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Override filename used for storage/display',
|
||||
|
||||
@@ -18,7 +18,6 @@ export const JinaBlock: BlockConfig<ReadUrlResponse | SearchResponse> = {
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Read URL', id: 'jina_read_url' },
|
||||
{ label: 'Search', id: 'jina_search' },
|
||||
@@ -30,48 +29,14 @@ export const JinaBlock: BlockConfig<ReadUrlResponse | SearchResponse> = {
|
||||
id: 'url',
|
||||
title: 'URL',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
required: true,
|
||||
placeholder: 'https://example.com',
|
||||
condition: { field: 'operation', value: 'jina_read_url' },
|
||||
},
|
||||
{
|
||||
id: 'targetSelector',
|
||||
title: 'Target Selector',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '#main-content (CSS selector)',
|
||||
condition: { field: 'operation', value: 'jina_read_url' },
|
||||
},
|
||||
{
|
||||
id: 'waitForSelector',
|
||||
title: 'Wait For Selector',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '.dynamic-content (CSS selector)',
|
||||
condition: { field: 'operation', value: 'jina_read_url' },
|
||||
},
|
||||
{
|
||||
id: 'removeSelector',
|
||||
title: 'Remove Selector',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'header, footer, .ad (CSS selector)',
|
||||
condition: { field: 'operation', value: 'jina_read_url' },
|
||||
},
|
||||
{
|
||||
id: 'timeout',
|
||||
title: 'Timeout (seconds)',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '30',
|
||||
condition: { field: 'operation', value: 'jina_read_url' },
|
||||
},
|
||||
{
|
||||
id: 'returnFormat',
|
||||
title: 'Return Format',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Markdown', id: 'markdown' },
|
||||
{ label: 'HTML', id: 'html' },
|
||||
@@ -86,7 +51,6 @@ export const JinaBlock: BlockConfig<ReadUrlResponse | SearchResponse> = {
|
||||
id: 'retainImages',
|
||||
title: 'Retain Images',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'All', id: 'all' },
|
||||
{ label: 'None', id: 'none' },
|
||||
@@ -94,80 +58,10 @@ export const JinaBlock: BlockConfig<ReadUrlResponse | SearchResponse> = {
|
||||
value: () => 'all',
|
||||
condition: { field: 'operation', value: 'jina_read_url' },
|
||||
},
|
||||
{
|
||||
id: 'engine',
|
||||
title: 'Engine',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Browser', id: 'browser' },
|
||||
{ label: 'Direct', id: 'direct' },
|
||||
{ label: 'CF Browser Rendering', id: 'cf-browser-rendering' },
|
||||
],
|
||||
value: () => 'browser',
|
||||
condition: { field: 'operation', value: 'jina_read_url' },
|
||||
},
|
||||
{
|
||||
id: 'proxy',
|
||||
title: 'Proxy (Country Code)',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'US, UK, auto, none',
|
||||
condition: { field: 'operation', value: 'jina_read_url' },
|
||||
},
|
||||
{
|
||||
id: 'proxyUrl',
|
||||
title: 'Proxy URL',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'http://proxy.example.com:8080',
|
||||
condition: { field: 'operation', value: 'jina_read_url' },
|
||||
},
|
||||
{
|
||||
id: 'setCookie',
|
||||
title: 'Cookies',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'session=abc123; token=xyz',
|
||||
condition: { field: 'operation', value: 'jina_read_url' },
|
||||
},
|
||||
{
|
||||
id: 'tokenBudget',
|
||||
title: 'Token Budget',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '10000',
|
||||
condition: { field: 'operation', value: 'jina_read_url' },
|
||||
},
|
||||
{
|
||||
id: 'cacheTolerance',
|
||||
title: 'Cache Tolerance (seconds)',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '3600',
|
||||
condition: { field: 'operation', value: 'jina_read_url' },
|
||||
},
|
||||
{
|
||||
id: 'locale',
|
||||
title: 'Locale',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'en-US',
|
||||
condition: { field: 'operation', value: 'jina_read_url' },
|
||||
},
|
||||
{
|
||||
id: 'baseUrl',
|
||||
title: 'Base URL',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [{ label: 'Final (Follow Redirects)', id: 'final' }],
|
||||
condition: { field: 'operation', value: 'jina_read_url' },
|
||||
},
|
||||
{
|
||||
id: 'readUrlOptions',
|
||||
title: 'Options',
|
||||
type: 'checkbox-list',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Use Reader LM v2 (3x cost)', id: 'useReaderLMv2' },
|
||||
{ label: 'Gather Links', id: 'gatherLinks' },
|
||||
@@ -187,56 +81,21 @@ export const JinaBlock: BlockConfig<ReadUrlResponse | SearchResponse> = {
|
||||
id: 'q',
|
||||
title: 'Search Query',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
required: true,
|
||||
placeholder: 'Enter your search query...',
|
||||
condition: { field: 'operation', value: 'jina_search' },
|
||||
},
|
||||
{
|
||||
id: 'gl',
|
||||
title: 'Country Code',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'US, UK, JP, etc.',
|
||||
condition: { field: 'operation', value: 'jina_search' },
|
||||
},
|
||||
{
|
||||
id: 'location',
|
||||
title: 'Location',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'New York, London, Tokyo',
|
||||
condition: { field: 'operation', value: 'jina_search' },
|
||||
},
|
||||
{
|
||||
id: 'hl',
|
||||
title: 'Language Code',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'en, es, fr, etc.',
|
||||
condition: { field: 'operation', value: 'jina_search' },
|
||||
},
|
||||
{
|
||||
id: 'num',
|
||||
title: 'Number of Results',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '5',
|
||||
condition: { field: 'operation', value: 'jina_search' },
|
||||
},
|
||||
{
|
||||
id: 'page',
|
||||
title: 'Page Number',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '1',
|
||||
condition: { field: 'operation', value: 'jina_search' },
|
||||
},
|
||||
{
|
||||
id: 'site',
|
||||
title: 'Site Restriction',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'jina.ai,github.com (comma-separated)',
|
||||
condition: { field: 'operation', value: 'jina_search' },
|
||||
},
|
||||
@@ -244,7 +103,6 @@ export const JinaBlock: BlockConfig<ReadUrlResponse | SearchResponse> = {
|
||||
id: 'searchReturnFormat',
|
||||
title: 'Return Format',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Markdown', id: 'markdown' },
|
||||
{ label: 'HTML', id: 'html' },
|
||||
@@ -257,7 +115,6 @@ export const JinaBlock: BlockConfig<ReadUrlResponse | SearchResponse> = {
|
||||
id: 'searchRetainImages',
|
||||
title: 'Retain Images',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'All', id: 'all' },
|
||||
{ label: 'None', id: 'none' },
|
||||
@@ -265,55 +122,10 @@ export const JinaBlock: BlockConfig<ReadUrlResponse | SearchResponse> = {
|
||||
value: () => 'all',
|
||||
condition: { field: 'operation', value: 'jina_search' },
|
||||
},
|
||||
{
|
||||
id: 'searchEngine',
|
||||
title: 'Engine',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Browser', id: 'browser' },
|
||||
{ label: 'Direct', id: 'direct' },
|
||||
],
|
||||
value: () => 'browser',
|
||||
condition: { field: 'operation', value: 'jina_search' },
|
||||
},
|
||||
{
|
||||
id: 'searchTimeout',
|
||||
title: 'Timeout (seconds)',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '30',
|
||||
condition: { field: 'operation', value: 'jina_search' },
|
||||
},
|
||||
{
|
||||
id: 'searchSetCookie',
|
||||
title: 'Cookies',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'session=abc123; token=xyz',
|
||||
condition: { field: 'operation', value: 'jina_search' },
|
||||
},
|
||||
{
|
||||
id: 'searchProxyUrl',
|
||||
title: 'Proxy URL',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'http://proxy.example.com:8080',
|
||||
condition: { field: 'operation', value: 'jina_search' },
|
||||
},
|
||||
{
|
||||
id: 'searchLocale',
|
||||
title: 'Locale',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'en-US',
|
||||
condition: { field: 'operation', value: 'jina_search' },
|
||||
},
|
||||
{
|
||||
id: 'searchOptions',
|
||||
title: 'Options',
|
||||
type: 'checkbox-list',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Include Favicons', id: 'withFavicon' },
|
||||
{ label: 'Gather Images', id: 'withImagesummary' },
|
||||
@@ -329,7 +141,6 @@ export const JinaBlock: BlockConfig<ReadUrlResponse | SearchResponse> = {
|
||||
id: 'apiKey',
|
||||
title: 'API Key',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
required: true,
|
||||
placeholder: 'Enter your Jina API key',
|
||||
password: true,
|
||||
@@ -351,46 +162,25 @@ export const JinaBlock: BlockConfig<ReadUrlResponse | SearchResponse> = {
|
||||
useReaderLMv2: { type: 'boolean', description: 'Use Reader LM v2 (3x cost)' },
|
||||
gatherLinks: { type: 'boolean', description: 'Gather page links' },
|
||||
jsonResponse: { type: 'boolean', description: 'JSON response format' },
|
||||
targetSelector: { type: 'string', description: 'CSS selector to target' },
|
||||
waitForSelector: { type: 'string', description: 'CSS selector to wait for' },
|
||||
removeSelector: { type: 'string', description: 'CSS selector to remove' },
|
||||
timeout: { type: 'number', description: 'Timeout in seconds' },
|
||||
withImagesummary: { type: 'boolean', description: 'Gather images' },
|
||||
retainImages: { type: 'string', description: 'Retain images setting' },
|
||||
returnFormat: { type: 'string', description: 'Output format' },
|
||||
withIframe: { type: 'boolean', description: 'Include iframes' },
|
||||
withShadowDom: { type: 'boolean', description: 'Include Shadow DOM' },
|
||||
setCookie: { type: 'string', description: 'Authentication cookies' },
|
||||
proxyUrl: { type: 'string', description: 'Proxy URL' },
|
||||
proxy: { type: 'string', description: 'Proxy country code' },
|
||||
engine: { type: 'string', description: 'Rendering engine' },
|
||||
tokenBudget: { type: 'number', description: 'Token budget' },
|
||||
noCache: { type: 'boolean', description: 'Bypass cache' },
|
||||
cacheTolerance: { type: 'number', description: 'Cache tolerance seconds' },
|
||||
withGeneratedAlt: { type: 'boolean', description: 'Generate image alt text' },
|
||||
baseUrl: { type: 'string', description: 'Follow redirects' },
|
||||
locale: { type: 'string', description: 'Browser locale' },
|
||||
robotsTxt: { type: 'string', description: 'Bot User-Agent' },
|
||||
dnt: { type: 'boolean', description: 'Do Not Track' },
|
||||
noGfm: { type: 'boolean', description: 'Disable GitHub Flavored Markdown' },
|
||||
// Search inputs
|
||||
q: { type: 'string', description: 'Search query' },
|
||||
gl: { type: 'string', description: 'Country code' },
|
||||
location: { type: 'string', description: 'City location' },
|
||||
hl: { type: 'string', description: 'Language code' },
|
||||
num: { type: 'number', description: 'Number of results' },
|
||||
page: { type: 'number', description: 'Page number' },
|
||||
site: { type: 'string', description: 'Site restriction' },
|
||||
withFavicon: { type: 'boolean', description: 'Include favicons' },
|
||||
withLinksummary: { type: 'boolean', description: 'Gather links' },
|
||||
respondWith: { type: 'string', description: 'Response mode' },
|
||||
searchReturnFormat: { type: 'string', description: 'Search output format' },
|
||||
searchRetainImages: { type: 'string', description: 'Search retain images' },
|
||||
searchEngine: { type: 'string', description: 'Search engine' },
|
||||
searchTimeout: { type: 'number', description: 'Search timeout' },
|
||||
searchSetCookie: { type: 'string', description: 'Search cookies' },
|
||||
searchProxyUrl: { type: 'string', description: 'Search proxy URL' },
|
||||
searchLocale: { type: 'string', description: 'Search locale' },
|
||||
},
|
||||
outputs: {
|
||||
// Read URL outputs
|
||||
|
||||
@@ -2,13 +2,16 @@ import { JiraIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import type { JiraResponse } from '@/tools/jira/types'
|
||||
import { getTrigger } from '@/triggers'
|
||||
|
||||
export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
type: 'jira',
|
||||
name: 'Jira',
|
||||
description: 'Interact with Jira',
|
||||
authMode: AuthMode.OAuth,
|
||||
longDescription: 'Integrate Jira into the workflow. Can read, write, and update issues.',
|
||||
triggerAllowed: true,
|
||||
longDescription:
|
||||
'Integrate Jira into the workflow. Can read, write, and update issues. Can also trigger workflows based on Jira webhook events.',
|
||||
docsLink: 'https://docs.sim.ai/tools/jira',
|
||||
category: 'tools',
|
||||
bgColor: '#E0E0E0',
|
||||
@@ -18,7 +21,6 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Read Issue', id: 'read' },
|
||||
{ label: 'Update Issue', id: 'update' },
|
||||
@@ -48,7 +50,6 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
id: 'domain',
|
||||
title: 'Domain',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
required: true,
|
||||
placeholder: 'Enter Jira domain (e.g., simstudio.atlassian.net)',
|
||||
},
|
||||
@@ -56,7 +57,6 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
id: 'credential',
|
||||
title: 'Jira Account',
|
||||
type: 'oauth-input',
|
||||
layout: 'full',
|
||||
required: true,
|
||||
provider: 'jira',
|
||||
serviceId: 'jira',
|
||||
@@ -80,7 +80,6 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
'read:user:jira',
|
||||
'read:field-configuration:jira',
|
||||
'read:issue-details:jira',
|
||||
// New scopes for expanded Jira operations
|
||||
'delete:issue:jira',
|
||||
'write:comment:jira',
|
||||
'read:comment:jira',
|
||||
@@ -100,7 +99,6 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
id: 'projectId',
|
||||
title: 'Select Project',
|
||||
type: 'project-selector',
|
||||
layout: 'full',
|
||||
canonicalParamId: 'projectId',
|
||||
provider: 'jira',
|
||||
serviceId: 'jira',
|
||||
@@ -113,7 +111,6 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
id: 'manualProjectId',
|
||||
title: 'Project ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
canonicalParamId: 'projectId',
|
||||
placeholder: 'Enter Jira project ID',
|
||||
dependsOn: ['credential', 'domain'],
|
||||
@@ -124,7 +121,6 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
id: 'issueKey',
|
||||
title: 'Select Issue',
|
||||
type: 'file-selector',
|
||||
layout: 'full',
|
||||
canonicalParamId: 'issueKey',
|
||||
provider: 'jira',
|
||||
serviceId: 'jira',
|
||||
@@ -158,7 +154,6 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
id: 'manualIssueKey',
|
||||
title: 'Issue Key',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
canonicalParamId: 'issueKey',
|
||||
placeholder: 'Enter Jira issue key',
|
||||
dependsOn: ['credential', 'domain', 'projectId', 'manualProjectId'],
|
||||
@@ -189,7 +184,6 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
id: 'summary',
|
||||
title: 'New Summary',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
required: true,
|
||||
placeholder: 'Enter new summary for the issue',
|
||||
dependsOn: ['issueKey'],
|
||||
@@ -199,7 +193,6 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
id: 'description',
|
||||
title: 'New Description',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter new description for the issue',
|
||||
dependsOn: ['issueKey'],
|
||||
condition: { field: 'operation', value: ['update', 'write'] },
|
||||
@@ -209,7 +202,6 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
id: 'deleteSubtasks',
|
||||
title: 'Delete Subtasks',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'No', id: 'false' },
|
||||
{ label: 'Yes', id: 'true' },
|
||||
@@ -222,7 +214,6 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
id: 'accountId',
|
||||
title: 'Account ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
required: true,
|
||||
placeholder: 'Enter user account ID to assign',
|
||||
condition: { field: 'operation', value: ['assign', 'add_watcher', 'remove_watcher'] },
|
||||
@@ -232,16 +223,14 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
id: 'transitionId',
|
||||
title: 'Transition ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
required: true,
|
||||
placeholder: 'Enter transition ID (e.g., 21)',
|
||||
condition: { field: 'operation', value: 'transition' },
|
||||
},
|
||||
{
|
||||
id: 'transitionComment',
|
||||
title: 'Comment (Optional)',
|
||||
title: 'Comment',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Add optional comment for transition',
|
||||
condition: { field: 'operation', value: 'transition' },
|
||||
},
|
||||
@@ -250,7 +239,6 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
id: 'jql',
|
||||
title: 'JQL Query',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
required: true,
|
||||
placeholder: 'Enter JQL query (e.g., project = PROJ AND status = "In Progress")',
|
||||
condition: { field: 'operation', value: 'search' },
|
||||
@@ -259,7 +247,6 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
id: 'maxResults',
|
||||
title: 'Max Results',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Maximum results to return (default: 50)',
|
||||
condition: { field: 'operation', value: ['search', 'get_comments', 'get_worklogs'] },
|
||||
},
|
||||
@@ -268,7 +255,6 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
id: 'commentBody',
|
||||
title: 'Comment Text',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
required: true,
|
||||
placeholder: 'Enter comment text',
|
||||
condition: { field: 'operation', value: ['add_comment', 'update_comment'] },
|
||||
@@ -277,7 +263,6 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
id: 'commentId',
|
||||
title: 'Comment ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
required: true,
|
||||
placeholder: 'Enter comment ID',
|
||||
condition: { field: 'operation', value: ['update_comment', 'delete_comment'] },
|
||||
@@ -287,7 +272,6 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
id: 'attachmentId',
|
||||
title: 'Attachment ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
required: true,
|
||||
placeholder: 'Enter attachment ID',
|
||||
condition: { field: 'operation', value: 'delete_attachment' },
|
||||
@@ -297,7 +281,6 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
id: 'timeSpentSeconds',
|
||||
title: 'Time Spent (seconds)',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
required: true,
|
||||
placeholder: 'Enter time in seconds (e.g., 3600 for 1 hour)',
|
||||
condition: { field: 'operation', value: 'add_worklog' },
|
||||
@@ -306,23 +289,20 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
id: 'timeSpentSecondsUpdate',
|
||||
title: 'Time Spent (seconds) - Optional',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter time in seconds (leave empty to keep unchanged)',
|
||||
condition: { field: 'operation', value: 'update_worklog' },
|
||||
},
|
||||
{
|
||||
id: 'worklogComment',
|
||||
title: 'Worklog Comment (Optional)',
|
||||
title: 'Worklog Comment',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter optional worklog comment',
|
||||
condition: { field: 'operation', value: ['add_worklog', 'update_worklog'] },
|
||||
},
|
||||
{
|
||||
id: 'started',
|
||||
title: 'Started At (Optional)',
|
||||
title: 'Started At',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'ISO timestamp (defaults to now)',
|
||||
condition: { field: 'operation', value: ['add_worklog', 'update_worklog'] },
|
||||
},
|
||||
@@ -330,7 +310,6 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
id: 'worklogId',
|
||||
title: 'Worklog ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
required: true,
|
||||
placeholder: 'Enter worklog ID',
|
||||
condition: { field: 'operation', value: ['update_worklog', 'delete_worklog'] },
|
||||
@@ -340,7 +319,6 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
id: 'inwardIssueKey',
|
||||
title: 'Inward Issue Key',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
required: true,
|
||||
placeholder: 'Enter inward issue key (e.g., PROJ-123)',
|
||||
condition: { field: 'operation', value: 'create_link' },
|
||||
@@ -349,7 +327,6 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
id: 'outwardIssueKey',
|
||||
title: 'Outward Issue Key',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
required: true,
|
||||
placeholder: 'Enter outward issue key (e.g., PROJ-456)',
|
||||
condition: { field: 'operation', value: 'create_link' },
|
||||
@@ -358,16 +335,14 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
id: 'linkType',
|
||||
title: 'Link Type',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
required: true,
|
||||
placeholder: 'Enter link type (e.g., "Blocks", "Relates")',
|
||||
condition: { field: 'operation', value: 'create_link' },
|
||||
},
|
||||
{
|
||||
id: 'linkComment',
|
||||
title: 'Link Comment (Optional)',
|
||||
title: 'Link Comment',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Add optional comment for the link',
|
||||
condition: { field: 'operation', value: 'create_link' },
|
||||
},
|
||||
@@ -375,11 +350,17 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
id: 'linkId',
|
||||
title: 'Link ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
required: true,
|
||||
placeholder: 'Enter link ID to delete',
|
||||
condition: { field: 'operation', value: 'delete_link' },
|
||||
},
|
||||
// Trigger SubBlocks
|
||||
...getTrigger('jira_issue_created').subBlocks,
|
||||
...getTrigger('jira_issue_updated').subBlocks,
|
||||
...getTrigger('jira_issue_deleted').subBlocks,
|
||||
...getTrigger('jira_issue_commented').subBlocks,
|
||||
...getTrigger('jira_worklog_created').subBlocks,
|
||||
...getTrigger('jira_webhook').subBlocks,
|
||||
],
|
||||
tools: {
|
||||
access: [
|
||||
@@ -860,5 +841,39 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
// jira_bulk_read outputs
|
||||
// Note: bulk_read returns an array in the output field, each item contains:
|
||||
// ts, issueKey, summary, description, status, assignee, created, updated
|
||||
|
||||
// Trigger outputs (from webhook events)
|
||||
event_type: { type: 'string', description: 'Webhook event type' },
|
||||
issue_id: { type: 'string', description: 'Issue ID from webhook' },
|
||||
issue_key: { type: 'string', description: 'Issue key from webhook' },
|
||||
project_key: { type: 'string', description: 'Project key from webhook' },
|
||||
project_name: { type: 'string', description: 'Project name from webhook' },
|
||||
issue_type_name: { type: 'string', description: 'Issue type from webhook' },
|
||||
priority_name: { type: 'string', description: 'Issue priority from webhook' },
|
||||
status_name: { type: 'string', description: 'Issue status from webhook' },
|
||||
assignee_name: { type: 'string', description: 'Assignee display name from webhook' },
|
||||
assignee_email: { type: 'string', description: 'Assignee email from webhook' },
|
||||
reporter_name: { type: 'string', description: 'Reporter display name from webhook' },
|
||||
reporter_email: { type: 'string', description: 'Reporter email from webhook' },
|
||||
comment_id: { type: 'string', description: 'Comment ID (for comment events)' },
|
||||
comment_body: { type: 'string', description: 'Comment text (for comment events)' },
|
||||
worklog_id: { type: 'string', description: 'Worklog ID (for worklog events)' },
|
||||
time_spent: { type: 'string', description: 'Time spent (for worklog events)' },
|
||||
changelog: { type: 'json', description: 'Changelog object (for update events)' },
|
||||
issue: { type: 'json', description: 'Complete issue object from webhook' },
|
||||
jira: { type: 'json', description: 'Complete webhook payload' },
|
||||
user: { type: 'json', description: 'User object who triggered the event' },
|
||||
webhook: { type: 'json', description: 'Webhook metadata' },
|
||||
},
|
||||
triggers: {
|
||||
enabled: true,
|
||||
available: [
|
||||
'jira_issue_created',
|
||||
'jira_issue_updated',
|
||||
'jira_issue_deleted',
|
||||
'jira_issue_commented',
|
||||
'jira_worklog_created',
|
||||
'jira_webhook',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,6 @@ export const LinkupBlock: BlockConfig<LinkupSearchToolResponse> = {
|
||||
id: 'q',
|
||||
title: 'Search Query',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter your search query',
|
||||
required: true,
|
||||
},
|
||||
@@ -26,7 +25,6 @@ export const LinkupBlock: BlockConfig<LinkupSearchToolResponse> = {
|
||||
id: 'outputType',
|
||||
title: 'Output Type',
|
||||
type: 'dropdown',
|
||||
layout: 'half',
|
||||
options: [
|
||||
{ label: 'Answer', id: 'sourcedAnswer' },
|
||||
{ label: 'Search', id: 'searchResults' },
|
||||
@@ -37,7 +35,6 @@ export const LinkupBlock: BlockConfig<LinkupSearchToolResponse> = {
|
||||
id: 'depth',
|
||||
title: 'Search Depth',
|
||||
type: 'dropdown',
|
||||
layout: 'half',
|
||||
options: [
|
||||
{ label: 'Standard', id: 'standard' },
|
||||
{ label: 'Deep', id: 'deep' },
|
||||
@@ -48,53 +45,45 @@ export const LinkupBlock: BlockConfig<LinkupSearchToolResponse> = {
|
||||
id: 'includeImages',
|
||||
title: 'Include Images',
|
||||
type: 'switch',
|
||||
layout: 'half',
|
||||
},
|
||||
{
|
||||
id: 'includeInlineCitations',
|
||||
title: 'Include Inline Citations',
|
||||
type: 'switch',
|
||||
layout: 'half',
|
||||
},
|
||||
{
|
||||
id: 'includeSources',
|
||||
title: 'Include Sources',
|
||||
type: 'switch',
|
||||
layout: 'half',
|
||||
},
|
||||
{
|
||||
id: 'fromDate',
|
||||
title: 'From Date',
|
||||
type: 'short-input',
|
||||
layout: 'half',
|
||||
placeholder: 'YYYY-MM-DD',
|
||||
},
|
||||
{
|
||||
id: 'toDate',
|
||||
title: 'To Date',
|
||||
type: 'short-input',
|
||||
layout: 'half',
|
||||
placeholder: 'YYYY-MM-DD',
|
||||
},
|
||||
{
|
||||
id: 'includeDomains',
|
||||
title: 'Include Domains',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'example.com, another.com (comma-separated)',
|
||||
},
|
||||
{
|
||||
id: 'excludeDomains',
|
||||
title: 'Exclude Domains',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'example.com, another.com (comma-separated)',
|
||||
},
|
||||
{
|
||||
id: 'apiKey',
|
||||
title: 'API Key',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter your Linkup API key',
|
||||
password: true,
|
||||
required: true,
|
||||
|
||||
@@ -41,7 +41,6 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Read Task', id: 'read_task' },
|
||||
{ label: 'Create Task', id: 'create_task' },
|
||||
@@ -62,7 +61,6 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
|
||||
id: 'credential',
|
||||
title: 'Microsoft Account',
|
||||
type: 'oauth-input',
|
||||
layout: 'full',
|
||||
provider: 'microsoft-planner',
|
||||
serviceId: 'microsoft-planner',
|
||||
requiredScopes: [
|
||||
@@ -77,23 +75,11 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
|
||||
placeholder: 'Select Microsoft account',
|
||||
},
|
||||
|
||||
// Group ID - for list_plans
|
||||
{
|
||||
id: 'groupId',
|
||||
title: 'Group ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter the Microsoft 365 group ID',
|
||||
condition: { field: 'operation', value: ['list_plans'] },
|
||||
dependsOn: ['credential'],
|
||||
},
|
||||
|
||||
// Plan ID - for various operations
|
||||
{
|
||||
id: 'planId',
|
||||
title: 'Plan ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter the plan ID',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -107,7 +93,6 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
|
||||
id: 'taskId',
|
||||
title: 'Task ID',
|
||||
type: 'file-selector',
|
||||
layout: 'full',
|
||||
placeholder: 'Select a task',
|
||||
provider: 'microsoft-planner',
|
||||
condition: { field: 'operation', value: ['read_task'] },
|
||||
@@ -121,7 +106,6 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
|
||||
id: 'manualTaskId',
|
||||
title: 'Manual Task ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter the task ID',
|
||||
condition: { field: 'operation', value: ['read_task'] },
|
||||
dependsOn: ['credential', 'planId'],
|
||||
@@ -134,7 +118,6 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
|
||||
id: 'taskIdForUpdate',
|
||||
title: 'Task ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter the task ID',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -149,7 +132,6 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
|
||||
id: 'bucketIdForRead',
|
||||
title: 'Bucket ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter the bucket ID',
|
||||
condition: { field: 'operation', value: ['read_bucket', 'update_bucket', 'delete_bucket'] },
|
||||
dependsOn: ['credential'],
|
||||
@@ -161,8 +143,8 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
|
||||
id: 'etag',
|
||||
title: 'ETag',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter the ETag from the resource (required for updates/deletes)',
|
||||
placeholder: 'Etag of the item',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
@@ -181,7 +163,6 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
|
||||
id: 'title',
|
||||
title: 'Task Title',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter the task title',
|
||||
condition: { field: 'operation', value: ['create_task', 'update_task'] },
|
||||
},
|
||||
@@ -191,7 +172,6 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
|
||||
id: 'name',
|
||||
title: 'Bucket Name',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter the bucket name',
|
||||
condition: { field: 'operation', value: ['create_bucket', 'update_bucket'] },
|
||||
},
|
||||
@@ -201,8 +181,7 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
|
||||
id: 'description',
|
||||
title: 'Description',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter task description (optional)',
|
||||
placeholder: 'Enter task description',
|
||||
condition: { field: 'operation', value: ['create_task', 'update_task_details'] },
|
||||
},
|
||||
|
||||
@@ -211,7 +190,6 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
|
||||
id: 'dueDateTime',
|
||||
title: 'Due Date',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter due date in ISO 8601 format (e.g., 2024-12-31T23:59:59Z)',
|
||||
condition: { field: 'operation', value: ['create_task', 'update_task'] },
|
||||
},
|
||||
@@ -221,7 +199,6 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
|
||||
id: 'startDateTime',
|
||||
title: 'Start Date',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter start date in ISO 8601 format (optional)',
|
||||
condition: { field: 'operation', value: ['update_task'] },
|
||||
},
|
||||
@@ -231,7 +208,6 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
|
||||
id: 'assigneeUserId',
|
||||
title: 'Assignee User ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter the user ID to assign this task to (optional)',
|
||||
condition: { field: 'operation', value: ['create_task', 'update_task'] },
|
||||
},
|
||||
@@ -241,7 +217,6 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
|
||||
id: 'bucketId',
|
||||
title: 'Bucket ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter the bucket ID to organize the task (optional)',
|
||||
condition: { field: 'operation', value: ['create_task', 'update_task'] },
|
||||
},
|
||||
@@ -251,7 +226,6 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
|
||||
id: 'priority',
|
||||
title: 'Priority',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter priority (0-10, optional)',
|
||||
condition: { field: 'operation', value: ['update_task'] },
|
||||
},
|
||||
@@ -261,7 +235,6 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
|
||||
id: 'percentComplete',
|
||||
title: 'Percent Complete',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter completion percentage (0-100, optional)',
|
||||
condition: { field: 'operation', value: ['update_task'] },
|
||||
},
|
||||
@@ -271,7 +244,6 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
|
||||
id: 'checklist',
|
||||
title: 'Checklist (JSON)',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter checklist as JSON object (optional)',
|
||||
condition: { field: 'operation', value: ['update_task_details'] },
|
||||
},
|
||||
@@ -281,7 +253,6 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
|
||||
id: 'references',
|
||||
title: 'References (JSON)',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter references as JSON object (optional)',
|
||||
condition: { field: 'operation', value: ['update_task_details'] },
|
||||
},
|
||||
@@ -291,7 +262,6 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
|
||||
id: 'previewType',
|
||||
title: 'Preview Type',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter preview type (automatic, noPreview, checklist, description, reference)',
|
||||
condition: { field: 'operation', value: ['update_task_details'] },
|
||||
},
|
||||
@@ -382,13 +352,7 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
|
||||
|
||||
// List Plans
|
||||
if (operation === 'list_plans') {
|
||||
if (!groupId?.trim()) {
|
||||
throw new Error('Group ID is required to list plans.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
groupId: groupId.trim(),
|
||||
}
|
||||
return baseParams
|
||||
}
|
||||
|
||||
// Read Plan
|
||||
@@ -476,9 +440,10 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
|
||||
// Read Task
|
||||
if (operation === 'read_task') {
|
||||
const readParams: MicrosoftPlannerBlockParams = { ...baseParams }
|
||||
const readTaskId = (taskId || manualTaskId || '').trim()
|
||||
|
||||
if (effectiveTaskId) {
|
||||
readParams.taskId = effectiveTaskId
|
||||
if (readTaskId) {
|
||||
readParams.taskId = readTaskId
|
||||
} else if (planId?.trim()) {
|
||||
readParams.planId = planId.trim()
|
||||
}
|
||||
@@ -642,6 +607,10 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
|
||||
previewType: { type: 'string', description: 'Preview type for task details' },
|
||||
},
|
||||
outputs: {
|
||||
message: {
|
||||
type: 'string',
|
||||
description: 'Success message from the operation',
|
||||
},
|
||||
task: {
|
||||
type: 'json',
|
||||
description:
|
||||
@@ -651,6 +620,14 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
|
||||
type: 'json',
|
||||
description: 'Array of Microsoft Planner tasks',
|
||||
},
|
||||
taskId: {
|
||||
type: 'string',
|
||||
description: 'ID of the task',
|
||||
},
|
||||
etag: {
|
||||
type: 'string',
|
||||
description: 'ETag of the resource - use this for update/delete operations',
|
||||
},
|
||||
plan: {
|
||||
type: 'json',
|
||||
description: 'The Microsoft Planner plan object',
|
||||
|
||||
@@ -21,7 +21,6 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Read Chat Messages', id: 'read_chat' },
|
||||
{ label: 'Write Chat Message', id: 'write_chat' },
|
||||
@@ -44,7 +43,6 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
|
||||
id: 'credential',
|
||||
title: 'Microsoft Account',
|
||||
type: 'oauth-input',
|
||||
layout: 'full',
|
||||
provider: 'microsoft-teams',
|
||||
serviceId: 'microsoft-teams',
|
||||
requiredScopes: [
|
||||
@@ -76,7 +74,6 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
|
||||
id: 'teamId',
|
||||
title: 'Select Team',
|
||||
type: 'file-selector',
|
||||
layout: 'full',
|
||||
canonicalParamId: 'teamId',
|
||||
provider: 'microsoft-teams',
|
||||
serviceId: 'microsoft-teams',
|
||||
@@ -101,7 +98,6 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
|
||||
id: 'manualTeamId',
|
||||
title: 'Team ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
canonicalParamId: 'teamId',
|
||||
placeholder: 'Enter team ID',
|
||||
mode: 'advanced',
|
||||
@@ -122,7 +118,6 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
|
||||
id: 'chatId',
|
||||
title: 'Select Chat',
|
||||
type: 'file-selector',
|
||||
layout: 'full',
|
||||
canonicalParamId: 'chatId',
|
||||
provider: 'microsoft-teams',
|
||||
serviceId: 'microsoft-teams',
|
||||
@@ -139,7 +134,6 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
|
||||
id: 'manualChatId',
|
||||
title: 'Chat ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
canonicalParamId: 'chatId',
|
||||
placeholder: 'Enter chat ID',
|
||||
mode: 'advanced',
|
||||
@@ -152,7 +146,6 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
|
||||
id: 'channelId',
|
||||
title: 'Select Channel',
|
||||
type: 'file-selector',
|
||||
layout: 'full',
|
||||
canonicalParamId: 'channelId',
|
||||
provider: 'microsoft-teams',
|
||||
serviceId: 'microsoft-teams',
|
||||
@@ -176,7 +169,6 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
|
||||
id: 'manualChannelId',
|
||||
title: 'Channel ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
canonicalParamId: 'channelId',
|
||||
placeholder: 'Enter channel ID',
|
||||
mode: 'advanced',
|
||||
@@ -196,7 +188,6 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
|
||||
id: 'messageId',
|
||||
title: 'Message ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter message ID',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -217,7 +208,6 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
|
||||
id: 'content',
|
||||
title: 'Message',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter message content',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -235,7 +225,6 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
|
||||
id: 'reactionType',
|
||||
title: 'Reaction',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter emoji (e.g., ❤️, 👍, 😊)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -248,7 +237,6 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
|
||||
id: 'attachmentFiles',
|
||||
title: 'Attachments',
|
||||
type: 'file-upload',
|
||||
layout: 'full',
|
||||
canonicalParamId: 'files',
|
||||
placeholder: 'Upload files to attach',
|
||||
condition: { field: 'operation', value: ['write_chat', 'write_channel'] },
|
||||
@@ -261,7 +249,6 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
|
||||
id: 'files',
|
||||
title: 'File Attachments',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
canonicalParamId: 'files',
|
||||
placeholder: 'Reference files from previous blocks',
|
||||
condition: { field: 'operation', value: ['write_chat', 'write_channel'] },
|
||||
|
||||
@@ -9,10 +9,10 @@ const logger = createLogger('OneDriveBlock')
|
||||
export const OneDriveBlock: BlockConfig<OneDriveResponse> = {
|
||||
type: 'onedrive',
|
||||
name: 'OneDrive',
|
||||
description: 'Create, upload, and list files',
|
||||
description: 'Create, upload, download, list, and delete files',
|
||||
authMode: AuthMode.OAuth,
|
||||
longDescription:
|
||||
'Integrate OneDrive into the workflow. Can create text and Excel files, upload files, and list files.',
|
||||
'Integrate OneDrive into the workflow. Can create text and Excel files, upload files, download files, list files, and delete files or folders.',
|
||||
docsLink: 'https://docs.sim.ai/tools/onedrive',
|
||||
category: 'tools',
|
||||
bgColor: '#E0E0E0',
|
||||
@@ -30,6 +30,7 @@ export const OneDriveBlock: BlockConfig<OneDriveResponse> = {
|
||||
{ label: 'Upload File', id: 'upload' },
|
||||
{ label: 'Download File', id: 'download' },
|
||||
{ label: 'List Files', id: 'list' },
|
||||
{ label: 'Delete File', id: 'delete' },
|
||||
],
|
||||
},
|
||||
// One Drive Credentials
|
||||
@@ -307,9 +308,51 @@ export const OneDriveBlock: BlockConfig<OneDriveResponse> = {
|
||||
placeholder: 'Optional: Override the filename',
|
||||
condition: { field: 'operation', value: 'download' },
|
||||
},
|
||||
// Delete File Fields - File Selector (basic mode)
|
||||
{
|
||||
id: 'fileSelector',
|
||||
title: 'Select File to Delete',
|
||||
type: 'file-selector',
|
||||
layout: 'full',
|
||||
canonicalParamId: 'fileId',
|
||||
provider: 'microsoft',
|
||||
serviceId: 'onedrive',
|
||||
requiredScopes: [
|
||||
'openid',
|
||||
'profile',
|
||||
'email',
|
||||
'Files.Read',
|
||||
'Files.ReadWrite',
|
||||
'offline_access',
|
||||
],
|
||||
mimeType: 'file', // Exclude folders, show only files
|
||||
placeholder: 'Select a file to delete',
|
||||
mode: 'basic',
|
||||
dependsOn: ['credential'],
|
||||
condition: { field: 'operation', value: 'delete' },
|
||||
required: true,
|
||||
},
|
||||
// Manual File ID input (advanced mode)
|
||||
{
|
||||
id: 'manualFileId',
|
||||
title: 'File ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
canonicalParamId: 'fileId',
|
||||
placeholder: 'Enter file or folder ID to delete',
|
||||
mode: 'advanced',
|
||||
condition: { field: 'operation', value: 'delete' },
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: ['onedrive_upload', 'onedrive_create_folder', 'onedrive_download', 'onedrive_list'],
|
||||
access: [
|
||||
'onedrive_upload',
|
||||
'onedrive_create_folder',
|
||||
'onedrive_download',
|
||||
'onedrive_list',
|
||||
'onedrive_delete',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => {
|
||||
switch (params.operation) {
|
||||
@@ -322,6 +365,8 @@ export const OneDriveBlock: BlockConfig<OneDriveResponse> = {
|
||||
return 'onedrive_download'
|
||||
case 'list':
|
||||
return 'onedrive_list'
|
||||
case 'delete':
|
||||
return 'onedrive_delete'
|
||||
default:
|
||||
throw new Error(`Invalid OneDrive operation: ${params.operation}`)
|
||||
}
|
||||
@@ -365,6 +410,9 @@ export const OneDriveBlock: BlockConfig<OneDriveResponse> = {
|
||||
pageSize: { type: 'number', description: 'Results per page' },
|
||||
},
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the operation was successful' },
|
||||
deleted: { type: 'boolean', description: 'Whether the file was deleted' },
|
||||
fileId: { type: 'string', description: 'The ID of the deleted file' },
|
||||
file: {
|
||||
type: 'json',
|
||||
description: 'The OneDrive file object, including details such as id, name, size, and more.',
|
||||
|
||||
@@ -103,6 +103,19 @@ export const OutlookBlock: BlockConfig<OutlookResponse> = {
|
||||
condition: { field: 'operation', value: ['send_outlook', 'draft_outlook'] },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'contentType',
|
||||
title: 'Content Type',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Plain Text', id: 'text' },
|
||||
{ label: 'HTML', id: 'html' },
|
||||
],
|
||||
condition: { field: 'operation', value: ['send_outlook', 'draft_outlook'] },
|
||||
value: () => 'text',
|
||||
required: false,
|
||||
},
|
||||
// File upload (basic mode)
|
||||
{
|
||||
id: 'attachmentFiles',
|
||||
@@ -406,6 +419,7 @@ export const OutlookBlock: BlockConfig<OutlookResponse> = {
|
||||
to: { type: 'string', description: 'Recipient email address' },
|
||||
subject: { type: 'string', description: 'Email subject' },
|
||||
body: { type: 'string', description: 'Email content' },
|
||||
contentType: { type: 'string', description: 'Content type (Text or HTML)' },
|
||||
attachmentFiles: { type: 'json', description: 'Files to attach (UI upload)' },
|
||||
attachments: { type: 'json', description: 'Files to attach (UserFile array)' },
|
||||
// Forward operation inputs
|
||||
|
||||
@@ -18,7 +18,6 @@ export const ParallelBlock: BlockConfig<ToolResponse> = {
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Search', id: 'search' },
|
||||
{ label: 'Extract from URLs', id: 'extract' },
|
||||
@@ -30,7 +29,6 @@ export const ParallelBlock: BlockConfig<ToolResponse> = {
|
||||
id: 'objective',
|
||||
title: 'Search Objective',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: "When was the United Nations established? Prefer UN's websites.",
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'search' },
|
||||
@@ -39,7 +37,6 @@ export const ParallelBlock: BlockConfig<ToolResponse> = {
|
||||
id: 'search_queries',
|
||||
title: 'Search Queries',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder:
|
||||
'Enter search queries separated by commas (e.g., "Founding year UN", "Year of founding United Nations")',
|
||||
required: false,
|
||||
@@ -49,7 +46,6 @@ export const ParallelBlock: BlockConfig<ToolResponse> = {
|
||||
id: 'urls',
|
||||
title: 'URLs',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder:
|
||||
'Enter URLs separated by commas (e.g., https://example.com, https://another.com)',
|
||||
required: true,
|
||||
@@ -59,7 +55,6 @@ export const ParallelBlock: BlockConfig<ToolResponse> = {
|
||||
id: 'extract_objective',
|
||||
title: 'Extract Objective',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'What information to extract from the URLs?',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'extract' },
|
||||
@@ -68,7 +63,6 @@ export const ParallelBlock: BlockConfig<ToolResponse> = {
|
||||
id: 'excerpts',
|
||||
title: 'Include Excerpts',
|
||||
type: 'dropdown',
|
||||
layout: 'half',
|
||||
options: [
|
||||
{ label: 'Yes', id: 'true' },
|
||||
{ label: 'No', id: 'false' },
|
||||
@@ -80,7 +74,6 @@ export const ParallelBlock: BlockConfig<ToolResponse> = {
|
||||
id: 'full_content',
|
||||
title: 'Include Full Content',
|
||||
type: 'dropdown',
|
||||
layout: 'half',
|
||||
options: [
|
||||
{ label: 'Yes', id: 'true' },
|
||||
{ label: 'No', id: 'false' },
|
||||
@@ -92,26 +85,14 @@ export const ParallelBlock: BlockConfig<ToolResponse> = {
|
||||
id: 'research_input',
|
||||
title: 'Research Query',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter your research question (up to 15,000 characters)',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'deep_research' },
|
||||
},
|
||||
{
|
||||
id: 'output_schema',
|
||||
title: 'Output Format',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder:
|
||||
'Enter "text" for markdown report, or describe desired output structure (leave empty for auto)',
|
||||
required: false,
|
||||
condition: { field: 'operation', value: 'deep_research' },
|
||||
},
|
||||
{
|
||||
id: 'include_domains',
|
||||
title: 'Include Domains',
|
||||
type: 'short-input',
|
||||
layout: 'half',
|
||||
placeholder: 'Comma-separated domains to include',
|
||||
required: false,
|
||||
condition: { field: 'operation', value: 'deep_research' },
|
||||
@@ -120,7 +101,6 @@ export const ParallelBlock: BlockConfig<ToolResponse> = {
|
||||
id: 'exclude_domains',
|
||||
title: 'Exclude Domains',
|
||||
type: 'short-input',
|
||||
layout: 'half',
|
||||
placeholder: 'Comma-separated domains to exclude',
|
||||
required: false,
|
||||
condition: { field: 'operation', value: 'deep_research' },
|
||||
@@ -129,7 +109,6 @@ export const ParallelBlock: BlockConfig<ToolResponse> = {
|
||||
id: 'processor',
|
||||
title: 'Processor',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Lite ($5/1K)', id: 'lite' },
|
||||
{ label: 'Base ($10/1K)', id: 'base' },
|
||||
@@ -147,7 +126,6 @@ export const ParallelBlock: BlockConfig<ToolResponse> = {
|
||||
id: 'max_results',
|
||||
title: 'Max Results',
|
||||
type: 'short-input',
|
||||
layout: 'half',
|
||||
placeholder: '5',
|
||||
condition: { field: 'operation', value: 'search' },
|
||||
},
|
||||
@@ -155,7 +133,6 @@ export const ParallelBlock: BlockConfig<ToolResponse> = {
|
||||
id: 'max_chars_per_result',
|
||||
title: 'Max Chars',
|
||||
type: 'short-input',
|
||||
layout: 'half',
|
||||
placeholder: '1500',
|
||||
condition: { field: 'operation', value: 'search' },
|
||||
},
|
||||
@@ -163,7 +140,6 @@ export const ParallelBlock: BlockConfig<ToolResponse> = {
|
||||
id: 'apiKey',
|
||||
title: 'API Key',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter your Parallel AI API key',
|
||||
password: true,
|
||||
required: true,
|
||||
@@ -244,7 +220,6 @@ export const ParallelBlock: BlockConfig<ToolResponse> = {
|
||||
excerpts: { type: 'boolean', description: 'Include excerpts' },
|
||||
full_content: { type: 'boolean', description: 'Include full content' },
|
||||
research_input: { type: 'string', description: 'Deep research query' },
|
||||
output_schema: { type: 'string', description: 'Output format specification' },
|
||||
include_domains: { type: 'string', description: 'Domains to include' },
|
||||
exclude_domains: { type: 'string', description: 'Domains to exclude' },
|
||||
processor: { type: 'string', description: 'Processing method' },
|
||||
|
||||
@@ -20,12 +20,10 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Get Posts', id: 'get_posts' },
|
||||
{ label: 'Get Comments', id: 'get_comments' },
|
||||
{ label: 'Get Controversial Posts', id: 'get_controversial' },
|
||||
{ label: 'Get Gilded Posts', id: 'get_gilded' },
|
||||
{ label: 'Search Subreddit', id: 'search' },
|
||||
{ label: 'Submit Post', id: 'submit_post' },
|
||||
{ label: 'Vote', id: 'vote' },
|
||||
@@ -44,7 +42,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'credential',
|
||||
title: 'Reddit Account',
|
||||
type: 'oauth-input',
|
||||
layout: 'full',
|
||||
provider: 'reddit',
|
||||
serviceId: 'reddit',
|
||||
requiredScopes: [
|
||||
@@ -74,11 +71,10 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'subreddit',
|
||||
title: 'Subreddit',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter subreddit name (without r/)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['get_posts', 'get_comments', 'get_controversial', 'get_gilded', 'search'],
|
||||
value: ['get_posts', 'get_comments', 'get_controversial', 'search'],
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
@@ -88,7 +84,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'sort',
|
||||
title: 'Sort By',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Hot', id: 'hot' },
|
||||
{ label: 'New', id: 'new' },
|
||||
@@ -105,7 +100,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'time',
|
||||
title: 'Time Filter (for Top sort)',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Day', id: 'day' },
|
||||
{ label: 'Week', id: 'week' },
|
||||
@@ -126,7 +120,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'limit',
|
||||
title: 'Max Posts',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '10',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -139,7 +132,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'postId',
|
||||
title: 'Post ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter post ID',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -151,7 +143,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'commentSort',
|
||||
title: 'Sort Comments By',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Confidence', id: 'confidence' },
|
||||
{ label: 'Top', id: 'top' },
|
||||
@@ -170,7 +161,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'commentLimit',
|
||||
title: 'Number of Comments',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '50',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -183,7 +173,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'controversialTime',
|
||||
title: 'Time Filter',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Hour', id: 'hour' },
|
||||
{ label: 'Day', id: 'day' },
|
||||
@@ -201,7 +190,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'controversialLimit',
|
||||
title: 'Max Posts',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '10',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -209,25 +197,11 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
},
|
||||
},
|
||||
|
||||
// Get Gilded specific fields
|
||||
{
|
||||
id: 'gildedLimit',
|
||||
title: 'Max Posts',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '10',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'get_gilded',
|
||||
},
|
||||
},
|
||||
|
||||
// Search specific fields
|
||||
{
|
||||
id: 'searchQuery',
|
||||
title: 'Search Query',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter search query',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -239,7 +213,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'searchSort',
|
||||
title: 'Sort By',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Relevance', id: 'relevance' },
|
||||
{ label: 'Hot', id: 'hot' },
|
||||
@@ -256,7 +229,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'searchTime',
|
||||
title: 'Time Filter',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Hour', id: 'hour' },
|
||||
{ label: 'Day', id: 'day' },
|
||||
@@ -274,7 +246,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'searchLimit',
|
||||
title: 'Max Results',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '10',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -287,7 +258,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'submitSubreddit',
|
||||
title: 'Subreddit',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter subreddit name (without r/)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -299,7 +269,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'title',
|
||||
title: 'Post Title',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter post title',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -311,7 +280,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'postType',
|
||||
title: 'Post Type',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Text Post', id: 'text' },
|
||||
{ label: 'Link Post', id: 'link' },
|
||||
@@ -327,7 +295,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'text',
|
||||
title: 'Post Text (Markdown)',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter post text in markdown format',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -342,7 +309,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'url',
|
||||
title: 'URL',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter URL to share',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -357,7 +323,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'nsfw',
|
||||
title: 'Mark as NSFW',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'No', id: 'false' },
|
||||
{ label: 'Yes', id: 'true' },
|
||||
@@ -372,7 +337,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'spoiler',
|
||||
title: 'Mark as Spoiler',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'No', id: 'false' },
|
||||
{ label: 'Yes', id: 'true' },
|
||||
@@ -389,7 +353,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'voteId',
|
||||
title: 'Post/Comment ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter thing ID (e.g., t3_xxxxx for post, t1_xxxxx for comment)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -401,7 +364,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'voteDirection',
|
||||
title: 'Vote Direction',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Upvote', id: '1' },
|
||||
{ label: 'Unvote', id: '0' },
|
||||
@@ -420,7 +382,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'saveId',
|
||||
title: 'Post/Comment ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter thing ID (e.g., t3_xxxxx for post, t1_xxxxx for comment)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -430,9 +391,8 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
},
|
||||
{
|
||||
id: 'saveCategory',
|
||||
title: 'Category (optional)',
|
||||
title: 'Category',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter category name',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -445,7 +405,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'replyParentId',
|
||||
title: 'Parent Post/Comment ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter thing ID to reply to (e.g., t3_xxxxx for post, t1_xxxxx for comment)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -457,7 +416,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'replyText',
|
||||
title: 'Reply Text (Markdown)',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter reply text in markdown format',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -471,7 +429,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'editThingId',
|
||||
title: 'Post/Comment ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter thing ID to edit (e.g., t3_xxxxx for post, t1_xxxxx for comment)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -483,7 +440,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'editText',
|
||||
title: 'New Text (Markdown)',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter new text in markdown format',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -497,7 +453,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'deleteId',
|
||||
title: 'Post/Comment ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter thing ID to delete (e.g., t3_xxxxx for post, t1_xxxxx for comment)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -511,7 +466,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'subscribeSubreddit',
|
||||
title: 'Subreddit',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter subreddit name (without r/)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -523,7 +477,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'subscribeAction',
|
||||
title: 'Action',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Subscribe', id: 'sub' },
|
||||
{ label: 'Unsubscribe', id: 'unsub' },
|
||||
@@ -541,7 +494,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
'reddit_get_posts',
|
||||
'reddit_get_comments',
|
||||
'reddit_get_controversial',
|
||||
'reddit_get_gilded',
|
||||
'reddit_search',
|
||||
'reddit_submit_post',
|
||||
'reddit_vote',
|
||||
@@ -564,10 +516,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
return 'reddit_get_controversial'
|
||||
}
|
||||
|
||||
if (operation === 'get_gilded') {
|
||||
return 'reddit_get_gilded'
|
||||
}
|
||||
|
||||
if (operation === 'search') {
|
||||
return 'reddit_search'
|
||||
}
|
||||
@@ -629,14 +577,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
}
|
||||
}
|
||||
|
||||
if (operation === 'get_gilded') {
|
||||
return {
|
||||
subreddit: rest.subreddit,
|
||||
limit: rest.gildedLimit ? Number.parseInt(rest.gildedLimit) : undefined,
|
||||
credential: credential,
|
||||
}
|
||||
}
|
||||
|
||||
if (operation === 'search') {
|
||||
return {
|
||||
subreddit: rest.subreddit,
|
||||
@@ -736,7 +676,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
commentLimit: { type: 'number', description: 'Maximum comments' },
|
||||
controversialTime: { type: 'string', description: 'Time filter for controversial posts' },
|
||||
controversialLimit: { type: 'number', description: 'Maximum controversial posts' },
|
||||
gildedLimit: { type: 'number', description: 'Maximum gilded posts' },
|
||||
searchQuery: { type: 'string', description: 'Search query text' },
|
||||
searchSort: { type: 'string', description: 'Search result sort order' },
|
||||
searchTime: { type: 'string', description: 'Time filter for search results' },
|
||||
|
||||
@@ -45,6 +45,18 @@ export const ResendBlock: BlockConfig<MailSendResult> = {
|
||||
placeholder: 'Email body content',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'contentType',
|
||||
title: 'Content Type',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Plain Text', id: 'text' },
|
||||
{ label: 'HTML', id: 'html' },
|
||||
],
|
||||
value: () => 'text',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
id: 'resendApiKey',
|
||||
title: 'Resend API Key',
|
||||
@@ -75,6 +87,7 @@ export const ResendBlock: BlockConfig<MailSendResult> = {
|
||||
to: { type: 'string', description: 'Recipient email address' },
|
||||
subject: { type: 'string', description: 'Email subject' },
|
||||
body: { type: 'string', description: 'Email body content' },
|
||||
contentType: { type: 'string', description: 'Content type (text or html)' },
|
||||
resendApiKey: { type: 'string', description: 'Resend API key for sending emails' },
|
||||
},
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ export const SlackBlock: BlockConfig<SlackResponse> = {
|
||||
},
|
||||
{
|
||||
id: 'threadTs',
|
||||
title: 'Thread Timestamp (Optional)',
|
||||
title: 'Thread Timestamp',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
canonicalParamId: 'thread_ts',
|
||||
|
||||
@@ -21,7 +21,6 @@ export const SupabaseBlock: BlockConfig<SupabaseResponse> = {
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
// Database Operations
|
||||
{ label: 'Get Many Rows', id: 'query' },
|
||||
@@ -55,7 +54,6 @@ export const SupabaseBlock: BlockConfig<SupabaseResponse> = {
|
||||
id: 'projectId',
|
||||
title: 'Project ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
password: true,
|
||||
placeholder: 'Your Supabase project ID (e.g., jdrkgepadsdopsntdlom)',
|
||||
required: true,
|
||||
@@ -64,7 +62,6 @@ export const SupabaseBlock: BlockConfig<SupabaseResponse> = {
|
||||
id: 'table',
|
||||
title: 'Table',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Name of the table',
|
||||
required: true,
|
||||
condition: {
|
||||
@@ -76,7 +73,6 @@ export const SupabaseBlock: BlockConfig<SupabaseResponse> = {
|
||||
id: 'apiKey',
|
||||
title: 'Service Role Secret',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Your Supabase service role secret key',
|
||||
password: true,
|
||||
required: true,
|
||||
@@ -86,7 +82,6 @@ export const SupabaseBlock: BlockConfig<SupabaseResponse> = {
|
||||
id: 'data',
|
||||
title: 'Data',
|
||||
type: 'code',
|
||||
layout: 'full',
|
||||
placeholder: '{\n "column1": "value1",\n "column2": "value2"\n}',
|
||||
condition: { field: 'operation', value: 'insert' },
|
||||
required: true,
|
||||
@@ -95,7 +90,6 @@ export const SupabaseBlock: BlockConfig<SupabaseResponse> = {
|
||||
id: 'data',
|
||||
title: 'Data',
|
||||
type: 'code',
|
||||
layout: 'full',
|
||||
placeholder: '{\n "column1": "value1",\n "column2": "value2"\n}',
|
||||
condition: { field: 'operation', value: 'update' },
|
||||
required: true,
|
||||
@@ -104,7 +98,6 @@ export const SupabaseBlock: BlockConfig<SupabaseResponse> = {
|
||||
id: 'data',
|
||||
title: 'Data',
|
||||
type: 'code',
|
||||
layout: 'full',
|
||||
placeholder: '{\n "column1": "value1",\n "column2": "value2"\n}',
|
||||
condition: { field: 'operation', value: 'upsert' },
|
||||
required: true,
|
||||
@@ -114,7 +107,6 @@ export const SupabaseBlock: BlockConfig<SupabaseResponse> = {
|
||||
id: 'filter',
|
||||
title: 'Filter (PostgREST syntax)',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'id=eq.123',
|
||||
condition: { field: 'operation', value: 'get_row' },
|
||||
required: true,
|
||||
@@ -183,7 +175,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'filter',
|
||||
title: 'Filter (PostgREST syntax)',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'id=eq.123',
|
||||
condition: { field: 'operation', value: 'update' },
|
||||
required: true,
|
||||
@@ -252,7 +243,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'filter',
|
||||
title: 'Filter (PostgREST syntax)',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'id=eq.123',
|
||||
condition: { field: 'operation', value: 'delete' },
|
||||
required: true,
|
||||
@@ -322,7 +312,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'filter',
|
||||
title: 'Filter (PostgREST syntax)',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'status=eq.active',
|
||||
condition: { field: 'operation', value: 'query' },
|
||||
wandConfig: {
|
||||
@@ -391,7 +380,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'orderBy',
|
||||
title: 'Order By',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'column_name (add DESC for descending)',
|
||||
condition: { field: 'operation', value: 'query' },
|
||||
},
|
||||
@@ -400,7 +388,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'limit',
|
||||
title: 'Limit',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '100',
|
||||
condition: { field: 'operation', value: 'query' },
|
||||
},
|
||||
@@ -409,7 +396,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'functionName',
|
||||
title: 'Function Name',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'match_documents',
|
||||
condition: { field: 'operation', value: 'vector_search' },
|
||||
required: true,
|
||||
@@ -418,24 +404,21 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'queryEmbedding',
|
||||
title: 'Query Embedding',
|
||||
type: 'code',
|
||||
layout: 'full',
|
||||
placeholder: '[0.1, 0.2, 0.3, ...]',
|
||||
condition: { field: 'operation', value: 'vector_search' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'matchThreshold',
|
||||
title: 'Match Threshold (optional)',
|
||||
title: 'Match Threshold',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '0.78',
|
||||
condition: { field: 'operation', value: 'vector_search' },
|
||||
},
|
||||
{
|
||||
id: 'matchCount',
|
||||
title: 'Match Count (optional)',
|
||||
title: 'Match Count',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '10',
|
||||
condition: { field: 'operation', value: 'vector_search' },
|
||||
},
|
||||
@@ -444,7 +427,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'functionName',
|
||||
title: 'Function Name',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'my_function_name',
|
||||
condition: { field: 'operation', value: 'rpc' },
|
||||
required: true,
|
||||
@@ -453,7 +435,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'params',
|
||||
title: 'Parameters (JSON)',
|
||||
type: 'code',
|
||||
layout: 'full',
|
||||
placeholder: '{\n "param1": "value1",\n "param2": "value2"\n}',
|
||||
condition: { field: 'operation', value: 'rpc' },
|
||||
},
|
||||
@@ -462,7 +443,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'column',
|
||||
title: 'Column to Search',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'content',
|
||||
condition: { field: 'operation', value: 'text_search' },
|
||||
required: true,
|
||||
@@ -471,7 +451,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'query',
|
||||
title: 'Search Query',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'search terms',
|
||||
condition: { field: 'operation', value: 'text_search' },
|
||||
required: true,
|
||||
@@ -480,7 +459,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'searchType',
|
||||
title: 'Search Type',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Websearch (natural language)', id: 'websearch' },
|
||||
{ label: 'Plain', id: 'plain' },
|
||||
@@ -493,7 +471,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'language',
|
||||
title: 'Language',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'english',
|
||||
condition: { field: 'operation', value: 'text_search' },
|
||||
},
|
||||
@@ -501,7 +478,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'limit',
|
||||
title: 'Limit',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '100',
|
||||
condition: { field: 'operation', value: 'text_search' },
|
||||
},
|
||||
@@ -510,15 +486,73 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'filter',
|
||||
title: 'Filter (PostgREST syntax)',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'status=eq.active',
|
||||
condition: { field: 'operation', value: 'count' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert in PostgREST API syntax. Generate PostgREST filter expressions based on the user's request.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the PostgREST filter expression. Do not include any explanations, markdown formatting, or additional text. Just the raw filter expression.
|
||||
|
||||
### POSTGREST FILTER SYNTAX
|
||||
PostgREST uses a specific syntax for filtering data. The format is:
|
||||
column=operator.value
|
||||
|
||||
### OPERATORS
|
||||
- **eq** - equals: \`id=eq.123\`
|
||||
- **neq** - not equals: \`status=neq.inactive\`
|
||||
- **gt** - greater than: \`age=gt.18\`
|
||||
- **gte** - greater than or equal: \`score=gte.80\`
|
||||
- **lt** - less than: \`price=lt.100\`
|
||||
- **lte** - less than or equal: \`rating=lte.5\`
|
||||
- **like** - pattern matching: \`name=like.*john*\`
|
||||
- **ilike** - case-insensitive like: \`email=ilike.*@gmail.com\`
|
||||
- **in** - in list: \`category=in.(tech,science,art)\`
|
||||
- **is** - is null/not null: \`deleted_at=is.null\`
|
||||
- **not** - negation: \`not.and=(status.eq.active,verified.eq.true)\`
|
||||
|
||||
### COMBINING FILTERS
|
||||
- **AND**: Use \`&\` or \`and=(...)\`: \`id=eq.123&status=eq.active\`
|
||||
- **OR**: Use \`or=(...)\`: \`or=(status.eq.active,status.eq.pending)\`
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Simple equality**: "Find user with ID 123"
|
||||
→ id=eq.123
|
||||
|
||||
**Text search**: "Find users with Gmail addresses"
|
||||
→ email=ilike.*@gmail.com
|
||||
|
||||
**Range filter**: "Find products under $50"
|
||||
→ price=lt.50
|
||||
|
||||
**Multiple conditions**: "Find active users over 18"
|
||||
→ age=gt.18&status=eq.active
|
||||
|
||||
**OR condition**: "Find active or pending orders"
|
||||
→ or=(status.eq.active,status.eq.pending)
|
||||
|
||||
**In list**: "Find posts in specific categories"
|
||||
→ category=in.(tech,science,health)
|
||||
|
||||
**Null check**: "Find users without a profile picture"
|
||||
→ profile_image=is.null
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY the PostgREST filter expression - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the filter condition...',
|
||||
generationType: 'postgrest',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'countType',
|
||||
title: 'Count Type',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Exact', id: 'exact' },
|
||||
{ label: 'Planned', id: 'planned' },
|
||||
@@ -532,7 +566,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'bucket',
|
||||
title: 'Bucket Name',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'my-bucket',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -556,7 +589,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'path',
|
||||
title: 'File Path',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'folder/file.jpg',
|
||||
condition: { field: 'operation', value: 'storage_upload' },
|
||||
required: true,
|
||||
@@ -565,7 +597,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'fileContent',
|
||||
title: 'File Content',
|
||||
type: 'code',
|
||||
layout: 'full',
|
||||
placeholder: 'Base64 encoded for binary files, or plain text',
|
||||
condition: { field: 'operation', value: 'storage_upload' },
|
||||
required: true,
|
||||
@@ -574,7 +605,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'contentType',
|
||||
title: 'Content Type (MIME)',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'image/jpeg',
|
||||
condition: { field: 'operation', value: 'storage_upload' },
|
||||
},
|
||||
@@ -582,7 +612,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'upsert',
|
||||
title: 'Upsert (overwrite if exists)',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'False', id: 'false' },
|
||||
{ label: 'True', id: 'true' },
|
||||
@@ -595,17 +624,22 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'path',
|
||||
title: 'File Path',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'folder/file.jpg',
|
||||
condition: { field: 'operation', value: 'storage_download' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'fileName',
|
||||
title: 'File Name Override',
|
||||
type: 'short-input',
|
||||
placeholder: 'my-file.jpg',
|
||||
condition: { field: 'operation', value: 'storage_download' },
|
||||
},
|
||||
// Storage List fields
|
||||
{
|
||||
id: 'path',
|
||||
title: 'Folder Path (optional)',
|
||||
title: 'Folder Path',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'folder/',
|
||||
condition: { field: 'operation', value: 'storage_list' },
|
||||
},
|
||||
@@ -613,7 +647,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'limit',
|
||||
title: 'Limit',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '100',
|
||||
condition: { field: 'operation', value: 'storage_list' },
|
||||
},
|
||||
@@ -621,7 +654,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'offset',
|
||||
title: 'Offset',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '0',
|
||||
condition: { field: 'operation', value: 'storage_list' },
|
||||
},
|
||||
@@ -629,7 +661,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'sortBy',
|
||||
title: 'Sort By',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Name', id: 'name' },
|
||||
{ label: 'Created At', id: 'created_at' },
|
||||
@@ -642,7 +673,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'sortOrder',
|
||||
title: 'Sort Order',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Ascending', id: 'asc' },
|
||||
{ label: 'Descending', id: 'desc' },
|
||||
@@ -654,7 +684,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'search',
|
||||
title: 'Search',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'search term',
|
||||
condition: { field: 'operation', value: 'storage_list' },
|
||||
},
|
||||
@@ -663,7 +692,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'paths',
|
||||
title: 'File Paths (JSON array)',
|
||||
type: 'code',
|
||||
layout: 'full',
|
||||
placeholder: '["folder/file1.jpg", "folder/file2.jpg"]',
|
||||
condition: { field: 'operation', value: 'storage_delete' },
|
||||
required: true,
|
||||
@@ -673,7 +701,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'fromPath',
|
||||
title: 'From Path',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'folder/old.jpg',
|
||||
condition: { field: 'operation', value: 'storage_move' },
|
||||
required: true,
|
||||
@@ -682,7 +709,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'toPath',
|
||||
title: 'To Path',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'newfolder/new.jpg',
|
||||
condition: { field: 'operation', value: 'storage_move' },
|
||||
required: true,
|
||||
@@ -692,7 +718,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'fromPath',
|
||||
title: 'From Path',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'folder/source.jpg',
|
||||
condition: { field: 'operation', value: 'storage_copy' },
|
||||
required: true,
|
||||
@@ -701,7 +726,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'toPath',
|
||||
title: 'To Path',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'folder/copy.jpg',
|
||||
condition: { field: 'operation', value: 'storage_copy' },
|
||||
required: true,
|
||||
@@ -711,7 +735,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'path',
|
||||
title: 'File Path',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'folder/file.jpg',
|
||||
condition: { field: 'operation', value: 'storage_get_public_url' },
|
||||
required: true,
|
||||
@@ -720,7 +743,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'download',
|
||||
title: 'Force Download',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'False', id: 'false' },
|
||||
{ label: 'True', id: 'true' },
|
||||
@@ -733,7 +755,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'path',
|
||||
title: 'File Path',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'folder/file.jpg',
|
||||
condition: { field: 'operation', value: 'storage_create_signed_url' },
|
||||
required: true,
|
||||
@@ -742,7 +763,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'expiresIn',
|
||||
title: 'Expires In (seconds)',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '3600',
|
||||
condition: { field: 'operation', value: 'storage_create_signed_url' },
|
||||
required: true,
|
||||
@@ -751,7 +771,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'download',
|
||||
title: 'Force Download',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'False', id: 'false' },
|
||||
{ label: 'True', id: 'true' },
|
||||
@@ -764,7 +783,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'isPublic',
|
||||
title: 'Public Bucket',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'False (Private)', id: 'false' },
|
||||
{ label: 'True (Public)', id: 'true' },
|
||||
@@ -776,7 +794,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'fileSizeLimit',
|
||||
title: 'File Size Limit (bytes)',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '52428800',
|
||||
condition: { field: 'operation', value: 'storage_create_bucket' },
|
||||
},
|
||||
@@ -784,7 +801,6 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
id: 'allowedMimeTypes',
|
||||
title: 'Allowed MIME Types (JSON array)',
|
||||
type: 'code',
|
||||
layout: 'full',
|
||||
placeholder: '["image/png", "image/jpeg"]',
|
||||
condition: { field: 'operation', value: 'storage_create_bucket' },
|
||||
},
|
||||
@@ -1041,6 +1057,7 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
path: { type: 'string', description: 'File path in storage' },
|
||||
fileContent: { type: 'string', description: 'File content (base64 for binary)' },
|
||||
contentType: { type: 'string', description: 'MIME type of the file' },
|
||||
fileName: { type: 'string', description: 'Optional filename override for downloaded file' },
|
||||
upsert: { type: 'boolean', description: 'Whether to overwrite existing file' },
|
||||
download: { type: 'boolean', description: 'Whether to force download' },
|
||||
paths: { type: 'array', description: 'Array of file paths' },
|
||||
@@ -1068,17 +1085,9 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
type: 'number',
|
||||
description: 'Row count for count operations',
|
||||
},
|
||||
fileContent: {
|
||||
type: 'string',
|
||||
description: 'Downloaded file content (base64 for binary files)',
|
||||
},
|
||||
contentType: {
|
||||
type: 'string',
|
||||
description: 'MIME type of downloaded file',
|
||||
},
|
||||
isBase64: {
|
||||
type: 'boolean',
|
||||
description: 'Whether file content is base64 encoded',
|
||||
file: {
|
||||
type: 'files',
|
||||
description: 'Downloaded file stored in execution files',
|
||||
},
|
||||
publicUrl: {
|
||||
type: 'string',
|
||||
|
||||
@@ -19,7 +19,6 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Search', id: 'tavily_search' },
|
||||
{ label: 'Extract Content', id: 'tavily_extract' },
|
||||
@@ -32,7 +31,6 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
id: 'query',
|
||||
title: 'Search Query',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter your search query...',
|
||||
condition: { field: 'operation', value: 'tavily_search' },
|
||||
required: true,
|
||||
@@ -41,7 +39,6 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
id: 'max_results',
|
||||
title: 'Max Results',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '5',
|
||||
condition: { field: 'operation', value: 'tavily_search' },
|
||||
},
|
||||
@@ -49,7 +46,6 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
id: 'topic',
|
||||
title: 'Topic',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'General', id: 'general' },
|
||||
{ label: 'News', id: 'news' },
|
||||
@@ -62,7 +58,6 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
id: 'search_depth',
|
||||
title: 'Search Depth',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Basic', id: 'basic' },
|
||||
{ label: 'Advanced', id: 'advanced' },
|
||||
@@ -74,57 +69,48 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
id: 'include_answer',
|
||||
title: 'Include Answer',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'None', id: 'false' },
|
||||
{ label: 'None', id: '' },
|
||||
{ label: 'Basic', id: 'basic' },
|
||||
{ label: 'Advanced', id: 'advanced' },
|
||||
],
|
||||
value: () => 'false',
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'tavily_search' },
|
||||
},
|
||||
{
|
||||
id: 'include_raw_content',
|
||||
title: 'Include Raw Content',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'None', id: 'false' },
|
||||
{ label: 'None', id: '' },
|
||||
{ label: 'Markdown', id: 'markdown' },
|
||||
{ label: 'Text', id: 'text' },
|
||||
],
|
||||
value: () => 'false',
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'tavily_search' },
|
||||
},
|
||||
{
|
||||
id: 'include_images',
|
||||
title: 'Include Images',
|
||||
type: 'switch',
|
||||
layout: 'full',
|
||||
value: () => 'false',
|
||||
condition: { field: 'operation', value: 'tavily_search' },
|
||||
},
|
||||
{
|
||||
id: 'include_image_descriptions',
|
||||
title: 'Include Image Descriptions',
|
||||
type: 'switch',
|
||||
layout: 'full',
|
||||
value: () => 'false',
|
||||
condition: { field: 'operation', value: 'tavily_search' },
|
||||
},
|
||||
{
|
||||
id: 'include_favicon',
|
||||
title: 'Include Favicon',
|
||||
type: 'switch',
|
||||
layout: 'full',
|
||||
value: () => 'false',
|
||||
condition: { field: 'operation', value: 'tavily_search' },
|
||||
},
|
||||
{
|
||||
id: 'time_range',
|
||||
title: 'Time Range',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'All Time', id: '' },
|
||||
{ label: 'Day', id: 'd' },
|
||||
@@ -139,7 +125,6 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
id: 'include_domains',
|
||||
title: 'Include Domains',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'example.com, another.com (comma-separated)',
|
||||
condition: { field: 'operation', value: 'tavily_search' },
|
||||
},
|
||||
@@ -147,7 +132,6 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
id: 'exclude_domains',
|
||||
title: 'Exclude Domains',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'example.com, another.com (comma-separated)',
|
||||
condition: { field: 'operation', value: 'tavily_search' },
|
||||
},
|
||||
@@ -155,7 +139,6 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
id: 'country',
|
||||
title: 'Country',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'US',
|
||||
condition: { field: 'operation', value: 'tavily_search' },
|
||||
},
|
||||
@@ -163,7 +146,6 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
id: 'urls',
|
||||
title: 'URL',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter URL to extract content from...',
|
||||
condition: { field: 'operation', value: 'tavily_extract' },
|
||||
required: true,
|
||||
@@ -172,7 +154,6 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
id: 'extract_depth',
|
||||
title: 'Extract Depth',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Basic', id: 'basic' },
|
||||
{ label: 'Advanced', id: 'advanced' },
|
||||
@@ -184,7 +165,6 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
id: 'format',
|
||||
title: 'Format',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Markdown', id: 'markdown' },
|
||||
{ label: 'Text', id: 'text' },
|
||||
@@ -196,23 +176,18 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
id: 'include_images',
|
||||
title: 'Include Images',
|
||||
type: 'switch',
|
||||
layout: 'full',
|
||||
value: () => 'false',
|
||||
condition: { field: 'operation', value: 'tavily_extract' },
|
||||
},
|
||||
{
|
||||
id: 'include_favicon',
|
||||
title: 'Include Favicon',
|
||||
type: 'switch',
|
||||
layout: 'full',
|
||||
value: () => 'false',
|
||||
condition: { field: 'operation', value: 'tavily_extract' },
|
||||
},
|
||||
{
|
||||
id: 'url',
|
||||
title: 'Website URL',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'https://example.com',
|
||||
condition: { field: 'operation', value: ['tavily_crawl', 'tavily_map'] },
|
||||
required: true,
|
||||
@@ -221,7 +196,6 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
id: 'instructions',
|
||||
title: 'Instructions',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Natural language directions for the crawler...',
|
||||
condition: { field: 'operation', value: ['tavily_crawl', 'tavily_map'] },
|
||||
},
|
||||
@@ -229,7 +203,6 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
id: 'max_depth',
|
||||
title: 'Max Depth',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '1',
|
||||
condition: { field: 'operation', value: ['tavily_crawl', 'tavily_map'] },
|
||||
},
|
||||
@@ -237,7 +210,6 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
id: 'max_breadth',
|
||||
title: 'Max Breadth',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '20',
|
||||
condition: { field: 'operation', value: ['tavily_crawl', 'tavily_map'] },
|
||||
},
|
||||
@@ -245,7 +217,6 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
id: 'limit',
|
||||
title: 'Limit',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: '50',
|
||||
condition: { field: 'operation', value: ['tavily_crawl', 'tavily_map'] },
|
||||
},
|
||||
@@ -253,7 +224,6 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
id: 'select_paths',
|
||||
title: 'Select Paths',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: '/docs/.*, /api/.* (regex patterns, comma-separated)',
|
||||
condition: { field: 'operation', value: ['tavily_crawl', 'tavily_map'] },
|
||||
},
|
||||
@@ -261,7 +231,6 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
id: 'select_domains',
|
||||
title: 'Select Domains',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: '^docs\\.example\\.com$ (regex patterns, comma-separated)',
|
||||
condition: { field: 'operation', value: ['tavily_crawl', 'tavily_map'] },
|
||||
},
|
||||
@@ -269,7 +238,6 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
id: 'exclude_paths',
|
||||
title: 'Exclude Paths',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: '/private/.*, /admin/.* (regex patterns, comma-separated)',
|
||||
condition: { field: 'operation', value: ['tavily_crawl', 'tavily_map'] },
|
||||
},
|
||||
@@ -277,7 +245,6 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
id: 'exclude_domains',
|
||||
title: 'Exclude Domains',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: '^private\\.example\\.com$ (regex patterns, comma-separated)',
|
||||
condition: { field: 'operation', value: ['tavily_crawl', 'tavily_map'] },
|
||||
},
|
||||
@@ -285,23 +252,18 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
id: 'allow_external',
|
||||
title: 'Allow External Links',
|
||||
type: 'switch',
|
||||
layout: 'full',
|
||||
value: () => 'true',
|
||||
condition: { field: 'operation', value: ['tavily_crawl', 'tavily_map'] },
|
||||
},
|
||||
{
|
||||
id: 'include_images',
|
||||
title: 'Include Images',
|
||||
type: 'switch',
|
||||
layout: 'full',
|
||||
value: () => 'false',
|
||||
condition: { field: 'operation', value: 'tavily_crawl' },
|
||||
},
|
||||
{
|
||||
id: 'extract_depth',
|
||||
title: 'Extract Depth',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Basic', id: 'basic' },
|
||||
{ label: 'Advanced', id: 'advanced' },
|
||||
@@ -313,7 +275,6 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
id: 'format',
|
||||
title: 'Format',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Markdown', id: 'markdown' },
|
||||
{ label: 'Text', id: 'text' },
|
||||
@@ -325,15 +286,12 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
id: 'include_favicon',
|
||||
title: 'Include Favicon',
|
||||
type: 'switch',
|
||||
layout: 'full',
|
||||
value: () => 'false',
|
||||
condition: { field: 'operation', value: 'tavily_crawl' },
|
||||
},
|
||||
{
|
||||
id: 'apiKey',
|
||||
title: 'API Key',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter your Tavily API key',
|
||||
password: true,
|
||||
required: true,
|
||||
|
||||
@@ -44,7 +44,7 @@ export const WorkflowBlock: BlockConfig = {
|
||||
},
|
||||
{
|
||||
id: 'input',
|
||||
title: 'Input Variable (Optional)',
|
||||
title: 'Input Variable',
|
||||
type: 'short-input',
|
||||
placeholder: 'Select a variable to pass to the child workflow',
|
||||
description: 'This variable will be available as start.input in the child workflow',
|
||||
|
||||
@@ -1798,13 +1798,13 @@ export function StripeIcon(props: SVGProps<SVGSVGElement>) {
|
||||
/>
|
||||
<path d='M194.9 33.9001H169.8V121.4H194.9V33.9001Z' fill='white' />
|
||||
<path
|
||||
fill-rule='evenodd'
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
d='M142.9 41.3001L141.3 33.9001H119.7V121.4H144.7V62.1001C150.6 54.4001 160.6 55.8001 163.7 56.9001V33.9001C160.5 32.7001 148.8 30.5001 142.9 41.3001Z'
|
||||
fill='white'
|
||||
/>
|
||||
<path
|
||||
fill-rule='evenodd'
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
d='M92.8999 12.2002L68.4999 17.4002L68.3999 97.5002C68.3999 112.3 79.4999 123.2 94.2999 123.2C102.5 123.2 108.5 121.7 111.8 119.9V99.6002C108.6 100.9 92.7999 105.5 92.7999 90.7002V55.2002H111.8V33.9002H92.7999L92.8999 12.2002Z'
|
||||
fill='white'
|
||||
|
||||
@@ -781,14 +781,24 @@ export const auth = betterAuth({
|
||||
scopes: [
|
||||
'read:confluence-content.all',
|
||||
'read:confluence-space.summary',
|
||||
'read:space:confluence',
|
||||
'read:space-details:confluence',
|
||||
'write:confluence-content',
|
||||
'write:confluence-space',
|
||||
'write:confluence-file',
|
||||
'read:page:confluence',
|
||||
'write:page:confluence',
|
||||
'read:comment:confluence',
|
||||
'read:content:confluence',
|
||||
'write:comment:confluence',
|
||||
'delete:comment:confluence',
|
||||
'read:attachment:confluence',
|
||||
'write:attachment:confluence',
|
||||
'delete:attachment:confluence',
|
||||
'delete:page:confluence',
|
||||
'read:label:confluence',
|
||||
'write:label:confluence',
|
||||
'search:confluence',
|
||||
'readonly:content.attachment:confluence',
|
||||
'read:me',
|
||||
'offline_access',
|
||||
],
|
||||
@@ -913,7 +923,6 @@ export const auth = betterAuth({
|
||||
'read:field-configuration:jira',
|
||||
'read:issue-details:jira',
|
||||
'read:issue-event:jira',
|
||||
// New scopes for expanded Jira operations
|
||||
'delete:issue:jira',
|
||||
'write:comment:jira',
|
||||
'read:comment:jira',
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { Chunk, StructuredDataOptions } from './types'
|
||||
|
||||
const logger = createLogger('StructuredDataChunker')
|
||||
|
||||
// Configuration for structured data chunking (CSV, XLSX, etc.)
|
||||
const STRUCTURED_CHUNKING_CONFIG = {
|
||||
// Target 2000-3000 tokens per chunk for better semantic meaning
|
||||
@@ -46,7 +49,7 @@ export class StructuredDataChunker {
|
||||
const optimalRowsPerChunk =
|
||||
StructuredDataChunker.calculateOptimalRowsPerChunk(estimatedTokensPerRow)
|
||||
|
||||
console.log(
|
||||
logger.info(
|
||||
`Structured data chunking: ${lines.length} rows, ~${estimatedTokensPerRow} tokens/row, ${optimalRowsPerChunk} rows/chunk`
|
||||
)
|
||||
|
||||
@@ -99,7 +102,7 @@ export class StructuredDataChunker {
|
||||
chunks.push(StructuredDataChunker.createChunk(chunkContent, chunkStartRow, lines.length - 1))
|
||||
}
|
||||
|
||||
console.log(`Created ${chunks.length} chunks from ${lines.length} rows of structured data`)
|
||||
logger.info(`Created ${chunks.length} chunks from ${lines.length} rows of structured data`)
|
||||
|
||||
return chunks
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { z } from 'zod'
|
||||
|
||||
// Tool IDs supported by the new Copilot runtime
|
||||
// Tool IDs supported by the Copilot runtime
|
||||
export const ToolIds = z.enum([
|
||||
'get_user_workflow',
|
||||
'edit_workflow',
|
||||
@@ -21,13 +21,10 @@ export const ToolIds = z.enum([
|
||||
'list_gdrive_files',
|
||||
'read_gdrive_file',
|
||||
'reason',
|
||||
// New tools
|
||||
'list_user_workflows',
|
||||
'get_workflow_from_name',
|
||||
// New variable tools
|
||||
'get_global_workflow_variables',
|
||||
'set_global_workflow_variables',
|
||||
// New
|
||||
'oauth_request_access',
|
||||
'get_trigger_blocks',
|
||||
])
|
||||
|
||||
@@ -359,14 +359,26 @@ export const OAUTH_PROVIDERS: Record<string, OAuthProviderConfig> = {
|
||||
scopes: [
|
||||
'read:confluence-content.all',
|
||||
'read:confluence-space.summary',
|
||||
'read:space:confluence',
|
||||
'read:space-details:confluence',
|
||||
'write:confluence-content',
|
||||
'write:confluence-space',
|
||||
'write:confluence-file',
|
||||
'read:page:confluence',
|
||||
'write:page:confluence',
|
||||
'read:comment:confluence',
|
||||
'write:comment:confluence',
|
||||
'delete:comment:confluence',
|
||||
'delete:attachment:confluence',
|
||||
'read:content:confluence',
|
||||
'delete:page:confluence',
|
||||
'write:label:confluence',
|
||||
'read:label:confluence',
|
||||
'read:attachment:confluence',
|
||||
'write:attachment:confluence',
|
||||
'read:label:confluence',
|
||||
'write:label:confluence',
|
||||
'search:confluence',
|
||||
'readonly:content.attachment:confluence',
|
||||
'read:me',
|
||||
'offline_access',
|
||||
],
|
||||
|
||||
@@ -384,6 +384,93 @@ export async function verifyProviderAuth(
|
||||
}
|
||||
}
|
||||
|
||||
if (foundWebhook.provider === 'linear') {
|
||||
const secret = providerConfig.secret as string | undefined
|
||||
|
||||
if (secret) {
|
||||
const signature = request.headers.get('Linear-Signature')
|
||||
|
||||
if (!signature) {
|
||||
logger.warn(`[${requestId}] Linear webhook missing signature header`)
|
||||
return new NextResponse('Unauthorized - Missing Linear signature', { status: 401 })
|
||||
}
|
||||
|
||||
const { validateLinearSignature } = await import('@/lib/webhooks/utils.server')
|
||||
|
||||
const isValidSignature = validateLinearSignature(secret, signature, rawBody)
|
||||
|
||||
if (!isValidSignature) {
|
||||
logger.warn(`[${requestId}] Linear signature verification failed`, {
|
||||
signatureLength: signature.length,
|
||||
secretLength: secret.length,
|
||||
})
|
||||
return new NextResponse('Unauthorized - Invalid Linear signature', { status: 401 })
|
||||
}
|
||||
|
||||
logger.debug(`[${requestId}] Linear signature verified successfully`)
|
||||
}
|
||||
}
|
||||
|
||||
if (foundWebhook.provider === 'jira') {
|
||||
const secret = providerConfig.secret as string | undefined
|
||||
|
||||
if (secret) {
|
||||
const signature = request.headers.get('X-Hub-Signature')
|
||||
|
||||
if (!signature) {
|
||||
logger.warn(`[${requestId}] Jira webhook missing signature header`)
|
||||
return new NextResponse('Unauthorized - Missing Jira signature', { status: 401 })
|
||||
}
|
||||
|
||||
const { validateJiraSignature } = await import('@/lib/webhooks/utils.server')
|
||||
|
||||
const isValidSignature = validateJiraSignature(secret, signature, rawBody)
|
||||
|
||||
if (!isValidSignature) {
|
||||
logger.warn(`[${requestId}] Jira signature verification failed`, {
|
||||
signatureLength: signature.length,
|
||||
secretLength: secret.length,
|
||||
})
|
||||
return new NextResponse('Unauthorized - Invalid Jira signature', { status: 401 })
|
||||
}
|
||||
|
||||
logger.debug(`[${requestId}] Jira signature verified successfully`)
|
||||
}
|
||||
}
|
||||
|
||||
if (foundWebhook.provider === 'github') {
|
||||
const secret = providerConfig.secret as string | undefined
|
||||
|
||||
if (secret) {
|
||||
// GitHub supports both SHA-256 (preferred) and SHA-1 (legacy)
|
||||
const signature256 = request.headers.get('X-Hub-Signature-256')
|
||||
const signature1 = request.headers.get('X-Hub-Signature')
|
||||
const signature = signature256 || signature1
|
||||
|
||||
if (!signature) {
|
||||
logger.warn(`[${requestId}] GitHub webhook missing signature header`)
|
||||
return new NextResponse('Unauthorized - Missing GitHub signature', { status: 401 })
|
||||
}
|
||||
|
||||
const { validateGitHubSignature } = await import('@/lib/webhooks/utils.server')
|
||||
|
||||
const isValidSignature = validateGitHubSignature(secret, signature, rawBody)
|
||||
|
||||
if (!isValidSignature) {
|
||||
logger.warn(`[${requestId}] GitHub signature verification failed`, {
|
||||
signatureLength: signature.length,
|
||||
secretLength: secret.length,
|
||||
usingSha256: !!signature256,
|
||||
})
|
||||
return new NextResponse('Unauthorized - Invalid GitHub signature', { status: 401 })
|
||||
}
|
||||
|
||||
logger.debug(`[${requestId}] GitHub signature verified successfully`, {
|
||||
usingSha256: !!signature256,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (foundWebhook.provider === 'generic') {
|
||||
if (providerConfig.requireAuth) {
|
||||
const configToken = providerConfig.token
|
||||
@@ -621,6 +708,35 @@ export async function queueWebhookExecution(
|
||||
}
|
||||
}
|
||||
|
||||
// Jira event filtering for event-specific triggers
|
||||
if (foundWebhook.provider === 'jira') {
|
||||
const providerConfig = (foundWebhook.providerConfig as Record<string, any>) || {}
|
||||
const triggerId = providerConfig.triggerId as string | undefined
|
||||
|
||||
if (triggerId && triggerId !== 'jira_webhook') {
|
||||
const webhookEvent = body.webhookEvent as string | undefined
|
||||
|
||||
const { isJiraEventMatch } = await import('@/triggers/jira/utils')
|
||||
|
||||
if (!isJiraEventMatch(triggerId, webhookEvent || '', body)) {
|
||||
logger.debug(
|
||||
`[${options.requestId}] Jira event mismatch for trigger ${triggerId}. Event: ${webhookEvent}. Skipping execution.`,
|
||||
{
|
||||
webhookId: foundWebhook.id,
|
||||
workflowId: foundWorkflow.id,
|
||||
triggerId,
|
||||
receivedEvent: webhookEvent,
|
||||
}
|
||||
)
|
||||
|
||||
// Return 200 OK to prevent Jira from retrying
|
||||
return NextResponse.json({
|
||||
message: 'Event type does not match trigger configuration. Ignoring.',
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const headers = Object.fromEntries(request.headers.entries())
|
||||
|
||||
// For Microsoft Teams Graph notifications, extract unique identifiers for idempotency
|
||||
|
||||
@@ -1233,6 +1233,70 @@ export async function formatWebhookInput(
|
||||
}
|
||||
}
|
||||
|
||||
if (foundWebhook.provider === 'linear') {
|
||||
// Linear webhook payload structure:
|
||||
// { action, type, webhookId, webhookTimestamp, organizationId, createdAt, actor, data, updatedFrom? }
|
||||
return {
|
||||
// Extract top-level fields from Linear payload
|
||||
action: body.action || '',
|
||||
type: body.type || '',
|
||||
webhookId: body.webhookId || '',
|
||||
webhookTimestamp: body.webhookTimestamp || 0,
|
||||
organizationId: body.organizationId || '',
|
||||
createdAt: body.createdAt || '',
|
||||
actor: body.actor || null,
|
||||
data: body.data || null,
|
||||
updatedFrom: body.updatedFrom || null,
|
||||
|
||||
// Keep webhook metadata
|
||||
webhook: {
|
||||
data: {
|
||||
provider: 'linear',
|
||||
path: foundWebhook.path,
|
||||
providerConfig: foundWebhook.providerConfig,
|
||||
payload: body,
|
||||
headers: Object.fromEntries(request.headers.entries()),
|
||||
method: request.method,
|
||||
},
|
||||
},
|
||||
workflowId: foundWorkflow.id,
|
||||
}
|
||||
}
|
||||
|
||||
// Jira webhook format
|
||||
if (foundWebhook.provider === 'jira') {
|
||||
const { extractIssueData, extractCommentData, extractWorklogData } = await import(
|
||||
'@/triggers/jira/utils'
|
||||
)
|
||||
|
||||
const providerConfig = (foundWebhook.providerConfig as Record<string, any>) || {}
|
||||
const triggerId = providerConfig.triggerId as string | undefined
|
||||
|
||||
let extractedData
|
||||
if (triggerId === 'jira_issue_commented') {
|
||||
extractedData = extractCommentData(body)
|
||||
} else if (triggerId === 'jira_worklog_created') {
|
||||
extractedData = extractWorklogData(body)
|
||||
} else {
|
||||
extractedData = extractIssueData(body)
|
||||
}
|
||||
|
||||
return {
|
||||
...extractedData,
|
||||
webhook: {
|
||||
data: {
|
||||
provider: 'jira',
|
||||
path: foundWebhook.path,
|
||||
providerConfig: foundWebhook.providerConfig,
|
||||
payload: body,
|
||||
headers: Object.fromEntries(request.headers.entries()),
|
||||
method: request.method,
|
||||
},
|
||||
},
|
||||
workflowId: foundWorkflow.id,
|
||||
}
|
||||
}
|
||||
|
||||
if (foundWebhook.provider === 'stripe') {
|
||||
return {
|
||||
...body,
|
||||
@@ -1279,25 +1343,21 @@ export function validateMicrosoftTeamsSignature(
|
||||
body: string
|
||||
): boolean {
|
||||
try {
|
||||
// Basic validation first
|
||||
if (!hmacSecret || !signature || !body) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if signature has correct format
|
||||
if (!signature.startsWith('HMAC ')) {
|
||||
return false
|
||||
}
|
||||
|
||||
const providedSignature = signature.substring(5) // Remove 'HMAC ' prefix
|
||||
const providedSignature = signature.substring(5)
|
||||
|
||||
// Compute HMAC SHA256 signature using Node.js crypto
|
||||
const crypto = require('crypto')
|
||||
const secretBytes = Buffer.from(hmacSecret, 'base64')
|
||||
const bodyBytes = Buffer.from(body, 'utf8')
|
||||
const computedHash = crypto.createHmac('sha256', secretBytes).update(bodyBytes).digest('base64')
|
||||
|
||||
// Constant-time comparison to prevent timing attacks
|
||||
if (computedHash.length !== providedSignature.length) {
|
||||
return false
|
||||
}
|
||||
@@ -1356,6 +1416,167 @@ export function validateTypeformSignature(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a Linear webhook request signature using HMAC SHA-256
|
||||
* @param secret - Linear webhook secret (plain text)
|
||||
* @param signature - Linear-Signature header value (hex-encoded HMAC SHA-256 signature)
|
||||
* @param body - Raw request body string
|
||||
* @returns Whether the signature is valid
|
||||
*/
|
||||
export function validateLinearSignature(secret: string, signature: string, body: string): boolean {
|
||||
try {
|
||||
if (!secret || !signature || !body) {
|
||||
logger.warn('Linear signature validation missing required fields', {
|
||||
hasSecret: !!secret,
|
||||
hasSignature: !!signature,
|
||||
hasBody: !!body,
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
const crypto = require('crypto')
|
||||
const computedHash = crypto.createHmac('sha256', secret).update(body, 'utf8').digest('hex')
|
||||
|
||||
logger.debug('Linear signature comparison', {
|
||||
computedSignature: `${computedHash.substring(0, 10)}...`,
|
||||
providedSignature: `${signature.substring(0, 10)}...`,
|
||||
computedLength: computedHash.length,
|
||||
providedLength: signature.length,
|
||||
match: computedHash === signature,
|
||||
})
|
||||
|
||||
if (computedHash.length !== signature.length) {
|
||||
return false
|
||||
}
|
||||
|
||||
let result = 0
|
||||
for (let i = 0; i < computedHash.length; i++) {
|
||||
result |= computedHash.charCodeAt(i) ^ signature.charCodeAt(i)
|
||||
}
|
||||
|
||||
return result === 0
|
||||
} catch (error) {
|
||||
logger.error('Error validating Linear signature:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a Jira webhook request signature using HMAC SHA-256
|
||||
* @param secret - Jira webhook secret (plain text)
|
||||
* @param signature - X-Hub-Signature header value (format: 'sha256=<hex>')
|
||||
* @param body - Raw request body string
|
||||
* @returns Whether the signature is valid
|
||||
*/
|
||||
export function validateJiraSignature(secret: string, signature: string, body: string): boolean {
|
||||
try {
|
||||
if (!secret || !signature || !body) {
|
||||
logger.warn('Jira signature validation missing required fields', {
|
||||
hasSecret: !!secret,
|
||||
hasSignature: !!signature,
|
||||
hasBody: !!body,
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
if (!signature.startsWith('sha256=')) {
|
||||
logger.warn('Jira signature has invalid format (expected sha256=)', {
|
||||
signaturePrefix: signature.substring(0, 10),
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
const providedSignature = signature.substring(7)
|
||||
|
||||
const crypto = require('crypto')
|
||||
const computedHash = crypto.createHmac('sha256', secret).update(body, 'utf8').digest('hex')
|
||||
|
||||
logger.debug('Jira signature comparison', {
|
||||
computedSignature: `${computedHash.substring(0, 10)}...`,
|
||||
providedSignature: `${providedSignature.substring(0, 10)}...`,
|
||||
computedLength: computedHash.length,
|
||||
providedLength: providedSignature.length,
|
||||
match: computedHash === providedSignature,
|
||||
})
|
||||
|
||||
if (computedHash.length !== providedSignature.length) {
|
||||
return false
|
||||
}
|
||||
|
||||
let result = 0
|
||||
for (let i = 0; i < computedHash.length; i++) {
|
||||
result |= computedHash.charCodeAt(i) ^ providedSignature.charCodeAt(i)
|
||||
}
|
||||
|
||||
return result === 0
|
||||
} catch (error) {
|
||||
logger.error('Error validating Jira signature:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a GitHub webhook request signature using HMAC SHA-256 or SHA-1
|
||||
* @param secret - GitHub webhook secret (plain text)
|
||||
* @param signature - X-Hub-Signature-256 or X-Hub-Signature header value (format: 'sha256=<hex>' or 'sha1=<hex>')
|
||||
* @param body - Raw request body string
|
||||
* @returns Whether the signature is valid
|
||||
*/
|
||||
export function validateGitHubSignature(secret: string, signature: string, body: string): boolean {
|
||||
try {
|
||||
if (!secret || !signature || !body) {
|
||||
logger.warn('GitHub signature validation missing required fields', {
|
||||
hasSecret: !!secret,
|
||||
hasSignature: !!signature,
|
||||
hasBody: !!body,
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
const crypto = require('crypto')
|
||||
let algorithm: 'sha256' | 'sha1'
|
||||
let providedSignature: string
|
||||
|
||||
if (signature.startsWith('sha256=')) {
|
||||
algorithm = 'sha256'
|
||||
providedSignature = signature.substring(7)
|
||||
} else if (signature.startsWith('sha1=')) {
|
||||
algorithm = 'sha1'
|
||||
providedSignature = signature.substring(5)
|
||||
} else {
|
||||
logger.warn('GitHub signature has invalid format', {
|
||||
signature: `${signature.substring(0, 10)}...`,
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
const computedHash = crypto.createHmac(algorithm, secret).update(body, 'utf8').digest('hex')
|
||||
|
||||
logger.debug('GitHub signature comparison', {
|
||||
algorithm,
|
||||
computedSignature: `${computedHash.substring(0, 10)}...`,
|
||||
providedSignature: `${providedSignature.substring(0, 10)}...`,
|
||||
computedLength: computedHash.length,
|
||||
providedLength: providedSignature.length,
|
||||
match: computedHash === providedSignature,
|
||||
})
|
||||
|
||||
if (computedHash.length !== providedSignature.length) {
|
||||
return false
|
||||
}
|
||||
|
||||
let result = 0
|
||||
for (let i = 0; i < computedHash.length; i++) {
|
||||
result |= computedHash.charCodeAt(i) ^ providedSignature.charCodeAt(i)
|
||||
}
|
||||
|
||||
return result === 0
|
||||
} catch (error) {
|
||||
logger.error('Error validating GitHub signature:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process webhook provider-specific verification
|
||||
*/
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export interface ConfluenceAddLabelParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
pageId: string
|
||||
labelName: string
|
||||
cloudId?: string
|
||||
}
|
||||
|
||||
export interface ConfluenceAddLabelResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
ts: string
|
||||
pageId: string
|
||||
labelName: string
|
||||
added: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export const confluenceAddLabelTool: ToolConfig<
|
||||
ConfluenceAddLabelParams,
|
||||
ConfluenceAddLabelResponse
|
||||
> = {
|
||||
id: 'confluence_add_label',
|
||||
name: 'Confluence Add Label',
|
||||
description: 'Add a label to a Confluence page.',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'confluence',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Confluence',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Your Confluence domain (e.g., yourcompany.atlassian.net)',
|
||||
},
|
||||
pageId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Confluence page ID to add label to',
|
||||
},
|
||||
labelName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Label name to add',
|
||||
},
|
||||
cloudId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description:
|
||||
'Confluence Cloud ID for the instance. If not provided, it will be fetched using the domain.',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => '/api/tools/confluence/labels',
|
||||
method: 'POST',
|
||||
headers: (params: ConfluenceAddLabelParams) => {
|
||||
return {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}
|
||||
},
|
||||
body: (params: ConfluenceAddLabelParams) => {
|
||||
return {
|
||||
domain: params.domain,
|
||||
accessToken: params.accessToken,
|
||||
cloudId: params.cloudId,
|
||||
pageId: params.pageId,
|
||||
labelName: params.labelName,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
pageId: data.pageId || '',
|
||||
labelName: data.labelName || '',
|
||||
added: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of operation' },
|
||||
pageId: { type: 'string', description: 'Page ID' },
|
||||
labelName: { type: 'string', description: 'Label name' },
|
||||
added: { type: 'boolean', description: 'Addition status' },
|
||||
},
|
||||
}
|
||||
@@ -63,7 +63,17 @@ export const confluenceGetSpaceTool: ToolConfig<
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => '/api/tools/confluence/space',
|
||||
url: (params: ConfluenceGetSpaceParams) => {
|
||||
const query = new URLSearchParams({
|
||||
domain: params.domain,
|
||||
accessToken: params.accessToken,
|
||||
spaceId: params.spaceId,
|
||||
})
|
||||
if (params.cloudId) {
|
||||
query.set('cloudId', params.cloudId)
|
||||
}
|
||||
return `/api/tools/confluence/space?${query.toString()}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params: ConfluenceGetSpaceParams) => {
|
||||
return {
|
||||
@@ -71,14 +81,6 @@ export const confluenceGetSpaceTool: ToolConfig<
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}
|
||||
},
|
||||
body: (params: ConfluenceGetSpaceParams) => {
|
||||
return {
|
||||
domain: params.domain,
|
||||
accessToken: params.accessToken,
|
||||
cloudId: params.cloudId,
|
||||
spaceId: params.spaceId,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
|
||||
@@ -1,51 +1,31 @@
|
||||
// Page operations
|
||||
|
||||
// Label operations
|
||||
import { confluenceAddLabelTool } from '@/tools/confluence/add_label'
|
||||
// Comment operations
|
||||
import { confluenceCreateCommentTool } from '@/tools/confluence/create_comment'
|
||||
import { confluenceCreatePageTool } from '@/tools/confluence/create_page'
|
||||
import { confluenceDeleteAttachmentTool } from '@/tools/confluence/delete_attachment'
|
||||
import { confluenceDeleteCommentTool } from '@/tools/confluence/delete_comment'
|
||||
import { confluenceDeletePageTool } from '@/tools/confluence/delete_page'
|
||||
// Space operations
|
||||
import { confluenceGetSpaceTool } from '@/tools/confluence/get_space'
|
||||
// Attachment operations
|
||||
import { confluenceListAttachmentsTool } from '@/tools/confluence/list_attachments'
|
||||
import { confluenceListCommentsTool } from '@/tools/confluence/list_comments'
|
||||
import { confluenceListLabelsTool } from '@/tools/confluence/list_labels'
|
||||
import { confluenceListSpacesTool } from '@/tools/confluence/list_spaces'
|
||||
import { confluenceRemoveLabelTool } from '@/tools/confluence/remove_label'
|
||||
import { confluenceRetrieveTool } from '@/tools/confluence/retrieve'
|
||||
// Search operations
|
||||
import { confluenceSearchTool } from '@/tools/confluence/search'
|
||||
import { confluenceUpdateTool } from '@/tools/confluence/update'
|
||||
import { confluenceUpdateCommentTool } from '@/tools/confluence/update_comment'
|
||||
|
||||
// Page operations exports
|
||||
export { confluenceRetrieveTool }
|
||||
export { confluenceUpdateTool }
|
||||
export { confluenceCreatePageTool }
|
||||
export { confluenceDeletePageTool }
|
||||
|
||||
// Search operations exports
|
||||
export { confluenceSearchTool }
|
||||
|
||||
// Comment operations exports
|
||||
export { confluenceCreateCommentTool }
|
||||
export { confluenceListCommentsTool }
|
||||
export { confluenceUpdateCommentTool }
|
||||
export { confluenceDeleteCommentTool }
|
||||
|
||||
// Attachment operations exports
|
||||
export { confluenceListAttachmentsTool }
|
||||
export { confluenceDeleteAttachmentTool }
|
||||
|
||||
// Label operations exports
|
||||
export { confluenceAddLabelTool }
|
||||
export { confluenceListLabelsTool }
|
||||
export { confluenceRemoveLabelTool }
|
||||
|
||||
// Space operations exports
|
||||
export { confluenceGetSpaceTool }
|
||||
export { confluenceListSpacesTool }
|
||||
export {
|
||||
confluenceRetrieveTool,
|
||||
confluenceUpdateTool,
|
||||
confluenceCreatePageTool,
|
||||
confluenceDeletePageTool,
|
||||
confluenceSearchTool,
|
||||
confluenceCreateCommentTool,
|
||||
confluenceListCommentsTool,
|
||||
confluenceUpdateCommentTool,
|
||||
confluenceDeleteCommentTool,
|
||||
confluenceListAttachmentsTool,
|
||||
confluenceDeleteAttachmentTool,
|
||||
confluenceListLabelsTool,
|
||||
confluenceGetSpaceTool,
|
||||
confluenceListSpacesTool,
|
||||
}
|
||||
|
||||
@@ -71,7 +71,18 @@ export const confluenceListAttachmentsTool: ToolConfig<
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => '/api/tools/confluence/attachments',
|
||||
url: (params: ConfluenceListAttachmentsParams) => {
|
||||
const query = new URLSearchParams({
|
||||
domain: params.domain,
|
||||
accessToken: params.accessToken,
|
||||
pageId: params.pageId,
|
||||
limit: String(params.limit || 25),
|
||||
})
|
||||
if (params.cloudId) {
|
||||
query.set('cloudId', params.cloudId)
|
||||
}
|
||||
return `/api/tools/confluence/attachments?${query.toString()}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params: ConfluenceListAttachmentsParams) => {
|
||||
return {
|
||||
|
||||
@@ -70,7 +70,18 @@ export const confluenceListCommentsTool: ToolConfig<
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => '/api/tools/confluence/comments',
|
||||
url: (params: ConfluenceListCommentsParams) => {
|
||||
const query = new URLSearchParams({
|
||||
domain: params.domain,
|
||||
accessToken: params.accessToken,
|
||||
pageId: params.pageId,
|
||||
limit: String(params.limit || 25),
|
||||
})
|
||||
if (params.cloudId) {
|
||||
query.set('cloudId', params.cloudId)
|
||||
}
|
||||
return `/api/tools/confluence/comments?${query.toString()}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params: ConfluenceListCommentsParams) => {
|
||||
return {
|
||||
|
||||
@@ -62,7 +62,17 @@ export const confluenceListLabelsTool: ToolConfig<
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => '/api/tools/confluence/labels',
|
||||
url: (params: ConfluenceListLabelsParams) => {
|
||||
const query = new URLSearchParams({
|
||||
domain: params.domain,
|
||||
accessToken: params.accessToken,
|
||||
pageId: params.pageId,
|
||||
})
|
||||
if (params.cloudId) {
|
||||
query.set('cloudId', params.cloudId)
|
||||
}
|
||||
return `/api/tools/confluence/labels?${query.toString()}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params: ConfluenceListLabelsParams) => {
|
||||
return {
|
||||
@@ -70,14 +80,6 @@ export const confluenceListLabelsTool: ToolConfig<
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}
|
||||
},
|
||||
body: (params: ConfluenceListLabelsParams) => {
|
||||
return {
|
||||
domain: params.domain,
|
||||
accessToken: params.accessToken,
|
||||
cloudId: params.cloudId,
|
||||
pageId: params.pageId,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
|
||||
@@ -64,7 +64,17 @@ export const confluenceListSpacesTool: ToolConfig<
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => '/api/tools/confluence/spaces',
|
||||
url: (params: ConfluenceListSpacesParams) => {
|
||||
const query = new URLSearchParams({
|
||||
domain: params.domain,
|
||||
accessToken: params.accessToken,
|
||||
limit: String(params.limit || 25),
|
||||
})
|
||||
if (params.cloudId) {
|
||||
query.set('cloudId', params.cloudId)
|
||||
}
|
||||
return `/api/tools/confluence/spaces?${query.toString()}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params: ConfluenceListSpacesParams) => {
|
||||
return {
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export interface ConfluenceRemoveLabelParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
pageId: string
|
||||
labelName: string
|
||||
cloudId?: string
|
||||
}
|
||||
|
||||
export interface ConfluenceRemoveLabelResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
ts: string
|
||||
pageId: string
|
||||
labelName: string
|
||||
removed: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export const confluenceRemoveLabelTool: ToolConfig<
|
||||
ConfluenceRemoveLabelParams,
|
||||
ConfluenceRemoveLabelResponse
|
||||
> = {
|
||||
id: 'confluence_remove_label',
|
||||
name: 'Confluence Remove Label',
|
||||
description: 'Remove a label from a Confluence page.',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'confluence',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Confluence',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Your Confluence domain (e.g., yourcompany.atlassian.net)',
|
||||
},
|
||||
pageId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Confluence page ID to remove label from',
|
||||
},
|
||||
labelName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Label name to remove',
|
||||
},
|
||||
cloudId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description:
|
||||
'Confluence Cloud ID for the instance. If not provided, it will be fetched using the domain.',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => '/api/tools/confluence/label',
|
||||
method: 'DELETE',
|
||||
headers: (params: ConfluenceRemoveLabelParams) => {
|
||||
return {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}
|
||||
},
|
||||
body: (params: ConfluenceRemoveLabelParams) => {
|
||||
return {
|
||||
domain: params.domain,
|
||||
accessToken: params.accessToken,
|
||||
cloudId: params.cloudId,
|
||||
pageId: params.pageId,
|
||||
labelName: params.labelName,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
pageId: data.pageId || '',
|
||||
labelName: data.labelName || '',
|
||||
removed: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of operation' },
|
||||
pageId: { type: 'string', description: 'Page ID' },
|
||||
labelName: { type: 'string', description: 'Label name' },
|
||||
removed: { type: 'boolean', description: 'Removal status' },
|
||||
},
|
||||
}
|
||||
@@ -53,9 +53,11 @@ export const discordEditMessageTool: ToolConfig<
|
||||
Authorization: `Bot ${params.botToken}`,
|
||||
}),
|
||||
body: (params: DiscordEditMessageParams) => {
|
||||
return {
|
||||
content: params.content,
|
||||
const body: any = {}
|
||||
if (params.content !== undefined && params.content !== null && params.content !== '') {
|
||||
body.content = params.content
|
||||
}
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -1,25 +1,17 @@
|
||||
// Existing tools
|
||||
|
||||
import { discordAddReactionTool } from '@/tools/discord/add_reaction'
|
||||
import { discordArchiveThreadTool } from '@/tools/discord/archive_thread'
|
||||
import { discordAssignRoleTool } from '@/tools/discord/assign_role'
|
||||
import { discordBanMemberTool } from '@/tools/discord/ban_member'
|
||||
// Channel operations
|
||||
import { discordCreateChannelTool } from '@/tools/discord/create_channel'
|
||||
// Invite operations
|
||||
import { discordCreateInviteTool } from '@/tools/discord/create_invite'
|
||||
// Role operations
|
||||
import { discordCreateRoleTool } from '@/tools/discord/create_role'
|
||||
// Thread operations
|
||||
import { discordCreateThreadTool } from '@/tools/discord/create_thread'
|
||||
// Webhook operations
|
||||
import { discordCreateWebhookTool } from '@/tools/discord/create_webhook'
|
||||
import { discordDeleteChannelTool } from '@/tools/discord/delete_channel'
|
||||
import { discordDeleteInviteTool } from '@/tools/discord/delete_invite'
|
||||
import { discordDeleteMessageTool } from '@/tools/discord/delete_message'
|
||||
import { discordDeleteRoleTool } from '@/tools/discord/delete_role'
|
||||
import { discordDeleteWebhookTool } from '@/tools/discord/delete_webhook'
|
||||
// Message operations
|
||||
import { discordEditMessageTool } from '@/tools/discord/edit_message'
|
||||
import { discordExecuteWebhookTool } from '@/tools/discord/execute_webhook'
|
||||
import { discordGetChannelTool } from '@/tools/discord/get_channel'
|
||||
@@ -30,7 +22,6 @@ import { discordGetServerTool } from '@/tools/discord/get_server'
|
||||
import { discordGetUserTool } from '@/tools/discord/get_user'
|
||||
import { discordGetWebhookTool } from '@/tools/discord/get_webhook'
|
||||
import { discordJoinThreadTool } from '@/tools/discord/join_thread'
|
||||
// Member operations
|
||||
import { discordKickMemberTool } from '@/tools/discord/kick_member'
|
||||
import { discordLeaveThreadTool } from '@/tools/discord/leave_thread'
|
||||
import { discordPinMessageTool } from '@/tools/discord/pin_message'
|
||||
@@ -44,45 +35,37 @@ import { discordUpdateMemberTool } from '@/tools/discord/update_member'
|
||||
import { discordUpdateRoleTool } from '@/tools/discord/update_role'
|
||||
|
||||
export {
|
||||
// Existing tools
|
||||
discordSendMessageTool,
|
||||
discordGetMessagesTool,
|
||||
discordGetServerTool,
|
||||
discordGetUserTool,
|
||||
// Message operations
|
||||
discordEditMessageTool,
|
||||
discordDeleteMessageTool,
|
||||
discordAddReactionTool,
|
||||
discordRemoveReactionTool,
|
||||
discordPinMessageTool,
|
||||
discordUnpinMessageTool,
|
||||
// Thread operations
|
||||
discordCreateThreadTool,
|
||||
discordJoinThreadTool,
|
||||
discordLeaveThreadTool,
|
||||
discordArchiveThreadTool,
|
||||
// Channel operations
|
||||
discordCreateChannelTool,
|
||||
discordUpdateChannelTool,
|
||||
discordDeleteChannelTool,
|
||||
discordGetChannelTool,
|
||||
// Role operations
|
||||
discordCreateRoleTool,
|
||||
discordUpdateRoleTool,
|
||||
discordDeleteRoleTool,
|
||||
discordAssignRoleTool,
|
||||
discordRemoveRoleTool,
|
||||
// Member operations
|
||||
discordKickMemberTool,
|
||||
discordBanMemberTool,
|
||||
discordUnbanMemberTool,
|
||||
discordGetMemberTool,
|
||||
discordUpdateMemberTool,
|
||||
// Invite operations
|
||||
discordCreateInviteTool,
|
||||
discordGetInviteTool,
|
||||
discordDeleteInviteTool,
|
||||
// Webhook operations
|
||||
discordCreateWebhookTool,
|
||||
discordExecuteWebhookTool,
|
||||
discordGetWebhookTool,
|
||||
|
||||
@@ -57,8 +57,10 @@ export const discordUpdateChannelTool: ToolConfig<
|
||||
}),
|
||||
body: (params: DiscordUpdateChannelParams) => {
|
||||
const body: any = {}
|
||||
if (params.name) body.name = params.name
|
||||
if (params.topic !== undefined) body.topic = params.topic
|
||||
if (params.name !== undefined && params.name !== null && params.name !== '')
|
||||
body.name = params.name
|
||||
if (params.topic !== undefined && params.topic !== null && params.topic !== '')
|
||||
body.topic = params.topic
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
@@ -60,9 +60,10 @@ export const discordUpdateMemberTool: ToolConfig<
|
||||
}),
|
||||
body: (params: DiscordUpdateMemberParams) => {
|
||||
const body: any = {}
|
||||
if (params.nick !== undefined) body.nick = params.nick
|
||||
if (params.mute !== undefined) body.mute = params.mute
|
||||
if (params.deaf !== undefined) body.deaf = params.deaf
|
||||
// Note: nick can be null to remove nickname, so we allow null but not empty string
|
||||
if (params.nick !== undefined && params.nick !== '') body.nick = params.nick
|
||||
if (params.mute !== undefined && params.mute !== null) body.mute = params.mute
|
||||
if (params.deaf !== undefined && params.deaf !== null) body.deaf = params.deaf
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
@@ -48,30 +48,6 @@ export const findSimilarLinksTool: ToolConfig<
|
||||
visibility: 'user-only',
|
||||
description: 'Exclude the source domain from results (default: false)',
|
||||
},
|
||||
startPublishedDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Filter results published after this date (ISO 8601 format, e.g., 2024-01-01)',
|
||||
},
|
||||
endPublishedDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Filter results published before this date (ISO 8601 format)',
|
||||
},
|
||||
startCrawlDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Filter results crawled after this date (ISO 8601 format)',
|
||||
},
|
||||
endCrawlDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Filter results crawled before this date (ISO 8601 format)',
|
||||
},
|
||||
category: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
@@ -137,12 +113,6 @@ export const findSimilarLinksTool: ToolConfig<
|
||||
body.excludeSourceDomain = params.excludeSourceDomain
|
||||
}
|
||||
|
||||
// Date filtering
|
||||
if (params.startPublishedDate) body.startPublishedDate = params.startPublishedDate
|
||||
if (params.endPublishedDate) body.endPublishedDate = params.endPublishedDate
|
||||
if (params.startCrawlDate) body.startCrawlDate = params.startCrawlDate
|
||||
if (params.endCrawlDate) body.endCrawlDate = params.endCrawlDate
|
||||
|
||||
// Category filtering
|
||||
if (params.category) body.category = params.category
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ export const researchTool: ToolConfig<ExaResearchParams, ExaResearchResponse> =
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.exa.ai/research/v1/',
|
||||
url: 'https://api.exa.ai/research/v1',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/json',
|
||||
@@ -61,7 +61,7 @@ export const researchTool: ToolConfig<ExaResearchParams, ExaResearchResponse> =
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
taskId: data.id,
|
||||
taskId: data.researchId,
|
||||
research: [],
|
||||
},
|
||||
}
|
||||
@@ -78,10 +78,11 @@ export const researchTool: ToolConfig<ExaResearchParams, ExaResearchResponse> =
|
||||
|
||||
while (elapsedTime < MAX_POLL_TIME_MS) {
|
||||
try {
|
||||
const statusResponse = await fetch(`https://api.exa.ai/research/v0/tasks/${taskId}`, {
|
||||
const statusResponse = await fetch(`https://api.exa.ai/research/v1/${taskId}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'x-api-key': params.apiKey,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
@@ -93,13 +94,17 @@ export const researchTool: ToolConfig<ExaResearchParams, ExaResearchResponse> =
|
||||
logger.info(`Exa research task ${taskId} status: ${taskData.status}`)
|
||||
|
||||
if (taskData.status === 'completed') {
|
||||
// The completed response contains output.content (text) and output.parsed (structured data)
|
||||
const content =
|
||||
taskData.output?.content || taskData.output?.parsed || 'Research completed successfully'
|
||||
|
||||
result.output = {
|
||||
research: taskData.data?.results || [
|
||||
research: [
|
||||
{
|
||||
title: 'Research Complete',
|
||||
url: '',
|
||||
summary: taskData.data || 'Research completed successfully',
|
||||
text: undefined,
|
||||
summary: typeof content === 'string' ? content : JSON.stringify(content, null, 2),
|
||||
text: typeof content === 'string' ? content : JSON.stringify(content, null, 2),
|
||||
publishedDate: undefined,
|
||||
author: undefined,
|
||||
score: 1.0,
|
||||
@@ -109,11 +114,11 @@ export const researchTool: ToolConfig<ExaResearchParams, ExaResearchResponse> =
|
||||
return result
|
||||
}
|
||||
|
||||
if (taskData.status === 'failed') {
|
||||
if (taskData.status === 'failed' || taskData.status === 'canceled') {
|
||||
return {
|
||||
...result,
|
||||
success: false,
|
||||
error: `Research task failed: ${taskData.error || 'Unknown error'}`,
|
||||
error: `Research task ${taskData.status}: ${taskData.error || 'Unknown error'}`,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,30 +45,6 @@ export const searchTool: ToolConfig<ExaSearchParams, ExaSearchResponse> = {
|
||||
visibility: 'user-only',
|
||||
description: 'Comma-separated list of domains to exclude from results',
|
||||
},
|
||||
startPublishedDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Filter results published after this date (ISO 8601 format, e.g., 2024-01-01)',
|
||||
},
|
||||
endPublishedDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Filter results published before this date (ISO 8601 format)',
|
||||
},
|
||||
startCrawlDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Filter results crawled after this date (ISO 8601 format)',
|
||||
},
|
||||
endCrawlDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Filter results crawled before this date (ISO 8601 format)',
|
||||
},
|
||||
category: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
@@ -139,12 +115,6 @@ export const searchTool: ToolConfig<ExaSearchParams, ExaSearchResponse> = {
|
||||
.filter((d: string) => d.length > 0)
|
||||
}
|
||||
|
||||
// Date filtering
|
||||
if (params.startPublishedDate) body.startPublishedDate = params.startPublishedDate
|
||||
if (params.endPublishedDate) body.endPublishedDate = params.endPublishedDate
|
||||
if (params.startCrawlDate) body.startCrawlDate = params.startCrawlDate
|
||||
if (params.endCrawlDate) body.endCrawlDate = params.endCrawlDate
|
||||
|
||||
// Category filtering
|
||||
if (params.category) body.category = params.category
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user