mirror of
https://github.com/simstudioai/sim.git
synced 2026-03-15 03:00:33 -04:00
Compare commits
22 Commits
feat/landi
...
v0.5.101
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4fd0989264 | ||
|
|
345a95f48d | ||
|
|
e07963f88c | ||
|
|
25c59e3e2e | ||
|
|
dde098e8e5 | ||
|
|
5ae0115444 | ||
|
|
fbafe204e5 | ||
|
|
ba7d6ff298 | ||
|
|
40016e79a1 | ||
|
|
e4fb8b2fdd | ||
|
|
d98545d554 | ||
|
|
fadbad4085 | ||
|
|
67f8a687f6 | ||
|
|
af592349d3 | ||
|
|
0d86ea01f0 | ||
|
|
115f04e989 | ||
|
|
34d92fae89 | ||
|
|
67aa4bb332 | ||
|
|
15ace5e63f | ||
|
|
fdca73679d | ||
|
|
da46a387c9 | ||
|
|
b7e377ec4b |
@@ -939,6 +939,25 @@ export function GoogleIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function DevinIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 500 500' fill='none' xmlns='http://www.w3.org/2000/svg'>
|
||||
<path
|
||||
d='M59.29,209.39l48.87,28.21c1.75,1.01,3.71,1.51,5.67,1.51c1.95,0,3.92-0.52,5.67-1.51l48.87-28.21c0,0,0.14-0.11,0.2-0.16c0.74-0.45,1.44-0.99,2.07-1.6c0.09-0.09,0.18-0.2,0.27-0.29c0.54-0.58,1.03-1.21,1.44-1.89c0.06-0.11,0.16-0.2,0.2-0.32c0.43-0.74,0.74-1.53,0.99-2.37c0.05-0.18,0.09-0.36,0.14-0.54c0.2-0.86,0.36-1.74,0.36-2.66v-28.21c0-10.89,5.87-21.03,15.3-26.48c9.42-5.45,21.15-5.44,30.59,0l24.43,14.11c0.79,0.45,1.62,0.77,2.47,1.01c0.18,0.05,0.37,0.11,0.54,0.16c0.83,0.2,1.69,0.32,2.54,0.34c0.05,0,0.09,0,0.11,0c0.09,0,0.18-0.05,0.26-0.05c0.79,0,1.58-0.11,2.34-0.32c0.14-0.03,0.27-0.05,0.4-0.09c0.83-0.23,1.64-0.57,2.41-0.99c0.06-0.05,0.16-0.05,0.23-0.09l48.87-28.21c3.51-2.03,5.67-5.76,5.67-9.81V64.52c0-4.05-2.16-7.78-5.67-9.81l-48.91-28.19c-3.51-2.03-7.81-2.03-11.32,0l-48.87,28.21c0,0-0.14,0.11-0.2,0.16c-0.74,0.45-1.44,0.99-2.07,1.6c-0.09,0.09-0.18,0.2-0.27,0.29c-0.54,0.58-1.03,1.21-1.44,1.89c-0.06,0.11-0.16,0.2-0.2,0.31c-0.43,0.74-0.74,1.53-0.99,2.37c-0.05,0.18-0.09,0.36-0.14,0.54c-0.2,0.86-0.36,1.74-0.36,2.66v28.21c0,10.89-5.87,21.03-15.3,26.5c-9.42,5.44-21.15,5.44-30.59,0l-24.42-14.1c-0.79-0.45-1.63-0.77-2.47-1.01c-0.18-0.05-0.36-0.11-0.54-0.16c-0.84-0.2-1.69-0.31-2.55-0.34c-0.14,0-0.25,0-0.38,0c-0.81,0-1.6,0.11-2.37,0.31c-0.14,0.02-0.25,0.05-0.38,0.09c-0.82,0.23-1.63,0.57-2.4,1c-0.06,0.05-0.16,0.05-0.23,0.09l-48.84,28.24c-3.51,2.03-5.67,5.76-5.67,9.81v56.42c0,4.05,2.16,7.78,5.67,9.81C59.29,209.41,59.29,209.39,59.29,209.39z'
|
||||
fill='#2A6DCE'
|
||||
/>
|
||||
<path
|
||||
d='M325.46,223.49c9.42-5.44,21.15-5.44,30.59,0l24.43,14.11c0.79,0.45,1.62,0.77,2.47,1.01c0.18,0.05,0.36,0.11,0.54,0.16c0.83,0.2,1.69,0.31,2.54,0.34c0.05,0,0.09,0,0.11,0c0.09,0,0.18-0.03,0.26-0.05c0.79,0,1.58-0.11,2.34-0.31c0.14-0.03,0.27-0.05,0.4-0.09c0.83-0.23,1.62-0.57,2.41-0.99c0.06-0.05,0.16-0.05,0.25-0.09l48.87-28.21c3.51-2.03,5.67-5.76,5.67-9.81v-56.43c0-4.05-2.16-7.78-5.67-9.81l-48.84-28.22c-3.51-2.03-7.81-2.03-11.32,0l-48.87,28.21c0,0-0.14,0.11-0.2,0.16c-0.74,0.45-1.44,0.99-2.07,1.6c-0.09,0.09-0.18,0.2-0.26,0.29c-0.54,0.58-1.03,1.21-1.44,1.89c-0.06,0.11-0.16,0.2-0.2,0.32c-0.43,0.74-0.74,1.53-0.99,2.37c-0.05,0.18-0.09,0.36-0.14,0.54c-0.2,0.86-0.36,1.74-0.36,2.66v28.21c0,10.89-5.87,21.03-15.3,26.5c-9.42,5.44-21.15,5.44-30.59,0l-24.43-14.11c-0.79-0.45-1.62-0.77-2.47-1.01c-0.18-0.05-0.36-0.11-0.54-0.16c-0.83-0.2-1.69-0.32-2.54-0.34c-0.14,0-0.25,0-0.38,0c-0.81,0-1.6,0.11-2.37,0.32c-0.14,0.03-0.25,0.05-0.38,0.09c-0.83,0.23-1.64,0.57-2.41,0.99c-0.06,0.05-0.16,0.05-0.23,0.09l-48.87,28.21c-3.51,2.03-5.67,5.76-5.67,9.81v56.43c0,4.05,2.16,7.78,5.67,9.81l48.87,28.21c0,0,0.16,0.05,0.23,0.09c0.77,0.43,1.58,0.77,2.41,0.99c0.14,0.05,0.27,0.05,0.4,0.09c0.77,0.18,1.55,0.29,2.34,0.32c0.09,0,0.18,0.05,0.27,0.05c0.05,0,0.09,0,0.11,0c0.86,0,1.69-0.14,2.54-0.34c0.18-0.05,0.36-0.09,0.54-0.16c0.86-0.25,1.69-0.57,2.47-1.01l24.43-14.11c9.42-5.44,21.15-5.44,30.59,0c9.42,5.44,15.3,15.59,15.3,26.48v28.21c0,0.92,0.14,1.8,0.36,2.66c0.05,0.18,0.09,0.36,0.14,0.54c0.25,0.83,0.56,1.62,0.99,2.37c0.06,0.11,0.14,0.2,0.2,0.31c0.4,0.68,0.9,1.31,1.44,1.89c0.09,0.09,0.18,0.2,0.26,0.29c0.61,0.6,1.31,1.12,2.07,1.6c0.06,0.05,0.11,0.11,0.2,0.16l48.87,28.21c1.75,1.01,3.72,1.51,5.67,1.51s3.92-0.52,5.67-1.51l48.87-28.21c3.51-2.03,5.67-5.76,5.67-9.81v-56.43c0-4.05-2.16-7.78-5.67-9.81l-48.87-28.21c0,0-0.16-0.05-0.23-0.09c-0.77-0.43-1.58-0.77-2.41-0.99c-0.14-0.05-0.25-0.05-0.38-0.09c-0.79-0.18-1.57-0.29-2.38-0.32c-0.11,0-0.25,0-0.36,0c-0.86,0-1.71,0.14-2.54,0.34c-0.18,0.05-0.34,0.09-0.52,0.16c-0.86,0.25-1.69,0.57-2.47,1.01l-24.43,14.11c-9.42,5.44-21.15,5.44-30.58,0c-9.42-5.44-15.3-15.59-15.3-26.5c0-10.91,5.87-21.03,15.3-26.48C325.55,223.49,325.46,223.49,325.46,223.49z'
|
||||
fill='#1DC19C'
|
||||
/>
|
||||
<path
|
||||
d='M304.5,369.22l-48.87-28.21c0,0-0.16-0.05-0.23-0.09c-0.77-0.43-1.57-0.77-2.41-0.99c-0.14-0.05-0.27-0.05-0.4-0.09c-0.79-0.18-1.57-0.29-2.37-0.32c-0.14,0-0.25,0-0.38,0c-0.86,0-1.71,0.14-2.54,0.34c-0.18,0.05-0.34,0.09-0.52,0.16c-0.86,0.25-1.69,0.57-2.47,1.01l-24.43,14.11c-9.42,5.44-21.15,5.44-30.58,0c-9.42-5.44-15.3-15.59-15.3-26.5v-28.22c0-0.92-0.14-1.8-0.36-2.66c-0.05-0.18-0.09-0.36-0.14-0.54c-0.25-0.83-0.57-1.62-0.99-2.37c-0.06-0.11-0.14-0.2-0.2-0.32c-0.4-0.68-0.9-1.31-1.44-1.89c-0.09-0.09-0.18-0.2-0.27-0.29c-0.6-0.6-1.31-1.12-2.07-1.6c-0.06-0.05-0.11-0.11-0.2-0.16l-48.87-28.21c-3.51-2.03-7.81-2.03-11.32,0L59.28,290.6c-3.51,2.03-5.67,5.76-5.67,9.81v56.43c0,4.05,2.16,7.78,5.67,9.81l48.87,28.21c0,0,0.16,0.06,0.23,0.09c0.77,0.43,1.55,0.77,2.38,0.99c0.14,0.05,0.27,0.06,0.4,0.09c0.77,0.18,1.55,0.29,2.34,0.32c0.09,0,0.18,0.05,0.29,0.05c0.05,0,0.09,0,0.14,0c0.86,0,1.69-0.14,2.52-0.34c0.18-0.05,0.36-0.09,0.54-0.16c0.86-0.25,1.69-0.57,2.47-1.01l24.43-14.11c9.42-5.44,21.15-5.44,30.59,0c9.42,5.44,15.3,15.59,15.3,26.48v28.21c0,0.92,0.14,1.8,0.36,2.66c0.05,0.18,0.09,0.36,0.14,0.54c0.25,0.83,0.57,1.62,0.99,2.37c0.06,0.11,0.14,0.2,0.2,0.32c0.4,0.68,0.9,1.31,1.44,1.89c0.09,0.09,0.18,0.2,0.27,0.29c0.61,0.61,1.31,1.12,2.07,1.6c0.06,0.05,0.11,0.11,0.2,0.16l48.87,28.21c1.75,1.01,3.71,1.51,5.67,1.51c1.96,0,3.92-0.52,5.67-1.51l48.87-28.21c3.51-2.03,5.67-5.76,5.67-9.81v-56.43c0-4.05-2.16-7.78-5.67-9.81L304.5,369.22z'
|
||||
fill='#1796E2'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function DiscordIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
@@ -1302,6 +1321,21 @@ export function GoogleCalendarIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function GoogleTasksIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 527.1 500' xmlns='http://www.w3.org/2000/svg'>
|
||||
<polygon
|
||||
fill='#0066DA'
|
||||
points='410.4,58.3 368.8,81.2 348.2,120.6 368.8,168.8 407.8,211 450,187.5 475.9,142.8 450,87.5'
|
||||
/>
|
||||
<path
|
||||
fill='#2684FC'
|
||||
d='M249.3,219.4l98.9-98.9c29.1,22.1,50.5,53.8,59.6,90.4L272.1,346.7c-12.2,12.2-32,12.2-44.2,0l-91.5-91.5 c-9.8-9.8-9.8-25.6,0-35.3l39-39c9.8-9.8,25.6-9.8,35.3,0L249.3,219.4z M519.8,63.6l-39.7-39.7c-9.7-9.7-25.6-9.7-35.3,0 l-34.4,34.4c27.5,23,49.9,51.8,65.5,84.5l43.9-43.9C529.6,89.2,529.6,73.3,519.8,63.6z M412.5,250c0,89.8-72.8,162.5-162.5,162.5 S87.5,339.8,87.5,250S160.2,87.5,250,87.5c36.9,0,70.9,12.3,98.2,33.1l62.2-62.2C367,21.9,311.1,0,250,0C111.9,0,0,111.9,0,250 s111.9,250,250,250s250-111.9,250-250c0-38.3-8.7-74.7-24.1-107.2L407.8,211C410.8,223.5,412.5,236.6,412.5,250z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function SupabaseIcon(props: SVGProps<SVGSVGElement>) {
|
||||
const id = useId()
|
||||
const gradient0 = `supabase_paint0_${id}`
|
||||
@@ -3430,6 +3464,23 @@ export const ResendIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
</svg>
|
||||
)
|
||||
|
||||
export const GoogleBigQueryIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'>
|
||||
<path
|
||||
d='M14.48 58.196L.558 34.082c-.744-1.288-.744-2.876 0-4.164L14.48 5.805c.743-1.287 2.115-2.08 3.6-2.082h27.857c1.48.007 2.845.8 3.585 2.082l13.92 24.113c.744 1.288.744 2.876 0 4.164L49.52 58.196c-.743 1.287-2.115 2.08-3.6 2.082H18.07c-1.483-.005-2.85-.798-3.593-2.082z'
|
||||
fill='#4386fa'
|
||||
/>
|
||||
<path
|
||||
d='M40.697 24.235s3.87 9.283-1.406 14.545-14.883 1.894-14.883 1.894L43.95 60.27h1.984c1.486-.002 2.858-.796 3.6-2.082L58.75 42.23z'
|
||||
opacity='.1'
|
||||
/>
|
||||
<path
|
||||
d='M45.267 43.23L41 38.953a.67.67 0 0 0-.158-.12 11.63 11.63 0 1 0-2.032 2.037.67.67 0 0 0 .113.15l4.277 4.277a.67.67 0 0 0 .947 0l1.12-1.12a.67.67 0 0 0 0-.947zM31.64 40.464a8.75 8.75 0 1 1 8.749-8.749 8.75 8.75 0 0 1-8.749 8.749zm-5.593-9.216v3.616c.557.983 1.363 1.803 2.338 2.375v-6.013zm4.375-2.998v9.772a6.45 6.45 0 0 0 2.338 0V28.25zm6.764 6.606v-2.142H34.85v4.5a6.43 6.43 0 0 0 2.338-2.368z'
|
||||
fill='#fff'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export const GoogleVaultIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 82 82'>
|
||||
<path
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
ConfluenceIcon,
|
||||
CursorIcon,
|
||||
DatadogIcon,
|
||||
DevinIcon,
|
||||
DiscordIcon,
|
||||
DocumentIcon,
|
||||
DropboxIcon,
|
||||
@@ -42,6 +43,7 @@ import {
|
||||
GitLabIcon,
|
||||
GmailIcon,
|
||||
GongIcon,
|
||||
GoogleBigQueryIcon,
|
||||
GoogleBooksIcon,
|
||||
GoogleCalendarIcon,
|
||||
GoogleDocsIcon,
|
||||
@@ -52,6 +54,7 @@ import {
|
||||
GoogleMapsIcon,
|
||||
GoogleSheetsIcon,
|
||||
GoogleSlidesIcon,
|
||||
GoogleTasksIcon,
|
||||
GoogleTranslateIcon,
|
||||
GoogleVaultIcon,
|
||||
GrafanaIcon,
|
||||
@@ -172,6 +175,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
confluence_v2: ConfluenceIcon,
|
||||
cursor_v2: CursorIcon,
|
||||
datadog: DatadogIcon,
|
||||
devin: DevinIcon,
|
||||
discord: DiscordIcon,
|
||||
dropbox: DropboxIcon,
|
||||
dspy: DsPyIcon,
|
||||
@@ -188,6 +192,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
gitlab: GitLabIcon,
|
||||
gmail_v2: GmailIcon,
|
||||
gong: GongIcon,
|
||||
google_bigquery: GoogleBigQueryIcon,
|
||||
google_books: GoogleBooksIcon,
|
||||
google_calendar_v2: GoogleCalendarIcon,
|
||||
google_docs: GoogleDocsIcon,
|
||||
@@ -198,6 +203,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
google_search: GoogleIcon,
|
||||
google_sheets_v2: GoogleSheetsIcon,
|
||||
google_slides_v2: GoogleSlidesIcon,
|
||||
google_tasks: GoogleTasksIcon,
|
||||
google_translate: GoogleTranslateIcon,
|
||||
google_vault: GoogleVaultIcon,
|
||||
grafana: GrafanaIcon,
|
||||
|
||||
@@ -1013,6 +1013,85 @@ Get details about a specific Confluence space.
|
||||
| ↳ `value` | string | Description text content |
|
||||
| ↳ `representation` | string | Content representation format \(e.g., plain, view, storage\) |
|
||||
|
||||
### `confluence_create_space`
|
||||
|
||||
Create a new Confluence space.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Confluence domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `name` | string | Yes | Name for the new space |
|
||||
| `key` | string | Yes | Unique key for the space \(uppercase, no spaces\) |
|
||||
| `description` | string | No | Description for the new space |
|
||||
| `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 | ISO 8601 timestamp of the operation |
|
||||
| `spaceId` | string | Created space ID |
|
||||
| `name` | string | Space name |
|
||||
| `key` | string | Space key |
|
||||
| `type` | string | Space type |
|
||||
| `status` | string | Space status |
|
||||
| `url` | string | URL to view the space |
|
||||
| `homepageId` | string | Homepage ID |
|
||||
| `description` | object | Space description |
|
||||
| ↳ `value` | string | Description text content |
|
||||
| ↳ `representation` | string | Content representation format \(e.g., plain, view, storage\) |
|
||||
|
||||
### `confluence_update_space`
|
||||
|
||||
Update a Confluence space name or description.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Confluence domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `spaceId` | string | Yes | ID of the space to update |
|
||||
| `name` | string | No | New name for the space |
|
||||
| `description` | string | No | New description for the space |
|
||||
| `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 | ISO 8601 timestamp of the operation |
|
||||
| `spaceId` | string | Updated space ID |
|
||||
| `name` | string | Space name |
|
||||
| `key` | string | Space key |
|
||||
| `type` | string | Space type |
|
||||
| `status` | string | Space status |
|
||||
| `url` | string | URL to view the space |
|
||||
| `description` | object | Space description |
|
||||
| ↳ `value` | string | Description text content |
|
||||
| ↳ `representation` | string | Content representation format \(e.g., plain, view, storage\) |
|
||||
|
||||
### `confluence_delete_space`
|
||||
|
||||
Delete a Confluence space.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Confluence domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `spaceId` | string | Yes | ID of the space to delete |
|
||||
| `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 | ISO 8601 timestamp of the operation |
|
||||
| `spaceId` | string | Deleted space ID |
|
||||
| `deleted` | boolean | Deletion status |
|
||||
|
||||
### `confluence_list_spaces`
|
||||
|
||||
List all Confluence spaces accessible to the user.
|
||||
@@ -1045,4 +1124,311 @@ List all Confluence spaces accessible to the user.
|
||||
| ↳ `representation` | string | Content representation format \(e.g., plain, view, storage\) |
|
||||
| `nextCursor` | string | Cursor for fetching the next page of results |
|
||||
|
||||
### `confluence_list_space_properties`
|
||||
|
||||
List properties on a Confluence space.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Confluence domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `spaceId` | string | Yes | Space ID to list properties for |
|
||||
| `limit` | number | No | Maximum number of properties to return \(default: 50, max: 250\) |
|
||||
| `cursor` | string | No | Pagination cursor from previous response |
|
||||
| `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 | ISO 8601 timestamp of the operation |
|
||||
| `properties` | array | Array of space properties |
|
||||
| ↳ `id` | string | Property ID |
|
||||
| ↳ `key` | string | Property key |
|
||||
| ↳ `value` | json | Property value |
|
||||
| `spaceId` | string | Space ID |
|
||||
| `nextCursor` | string | Cursor for fetching the next page of results |
|
||||
|
||||
### `confluence_create_space_property`
|
||||
|
||||
Create a property on a Confluence space.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Confluence domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `spaceId` | string | Yes | Space ID to create the property on |
|
||||
| `key` | string | Yes | Property key/name |
|
||||
| `value` | json | No | Property value \(JSON\) |
|
||||
| `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 | ISO 8601 timestamp of the operation |
|
||||
| `propertyId` | string | Created property ID |
|
||||
| `key` | string | Property key |
|
||||
| `value` | json | Property value |
|
||||
| `spaceId` | string | Space ID |
|
||||
|
||||
### `confluence_delete_space_property`
|
||||
|
||||
Delete a property from a Confluence space.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Confluence domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `spaceId` | string | Yes | Space ID the property belongs to |
|
||||
| `propertyId` | string | Yes | Property ID to delete |
|
||||
| `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 | ISO 8601 timestamp of the operation |
|
||||
| `spaceId` | string | Space ID |
|
||||
| `propertyId` | string | Deleted property ID |
|
||||
| `deleted` | boolean | Deletion status |
|
||||
|
||||
### `confluence_list_space_permissions`
|
||||
|
||||
List permissions for a Confluence space.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Confluence domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `spaceId` | string | Yes | Space ID to list permissions for |
|
||||
| `limit` | number | No | Maximum number of permissions to return \(default: 50, max: 250\) |
|
||||
| `cursor` | string | No | Pagination cursor from previous response |
|
||||
| `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 | ISO 8601 timestamp of the operation |
|
||||
| `permissions` | array | Array of space permissions |
|
||||
| ↳ `id` | string | Permission ID |
|
||||
| ↳ `principalType` | string | Principal type \(user, group, role\) |
|
||||
| ↳ `principalId` | string | Principal ID |
|
||||
| ↳ `operationKey` | string | Operation key \(read, create, delete, etc.\) |
|
||||
| ↳ `operationTargetType` | string | Target type \(page, blogpost, space, etc.\) |
|
||||
| ↳ `anonymousAccess` | boolean | Whether anonymous access is allowed |
|
||||
| ↳ `unlicensedAccess` | boolean | Whether unlicensed access is allowed |
|
||||
| `spaceId` | string | Space ID |
|
||||
| `nextCursor` | string | Cursor for fetching the next page of results |
|
||||
|
||||
### `confluence_get_page_descendants`
|
||||
|
||||
Get all descendants of a Confluence page recursively.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Confluence domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `pageId` | string | Yes | Page ID to get descendants for |
|
||||
| `limit` | number | No | Maximum number of descendants to return \(default: 50, max: 250\) |
|
||||
| `cursor` | string | No | Pagination cursor from previous response |
|
||||
| `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 | ISO 8601 timestamp of the operation |
|
||||
| `descendants` | array | Array of descendant pages |
|
||||
| ↳ `id` | string | Page ID |
|
||||
| ↳ `title` | string | Page title |
|
||||
| ↳ `type` | string | Content type \(page, whiteboard, database, etc.\) |
|
||||
| ↳ `status` | string | Page status |
|
||||
| ↳ `spaceId` | string | Space ID |
|
||||
| ↳ `parentId` | string | Parent page ID |
|
||||
| ↳ `childPosition` | number | Position among siblings |
|
||||
| ↳ `depth` | number | Depth in the hierarchy |
|
||||
| `pageId` | string | Parent page ID |
|
||||
| `nextCursor` | string | Cursor for fetching the next page of results |
|
||||
|
||||
### `confluence_list_tasks`
|
||||
|
||||
List inline tasks from Confluence. Optionally filter by page, space, assignee, or status.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Confluence domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `pageId` | string | No | Filter tasks by page ID |
|
||||
| `spaceId` | string | No | Filter tasks by space ID |
|
||||
| `assignedTo` | string | No | Filter tasks by assignee account ID |
|
||||
| `status` | string | No | Filter tasks by status \(complete or incomplete\) |
|
||||
| `limit` | number | No | Maximum number of tasks to return \(default: 50, max: 250\) |
|
||||
| `cursor` | string | No | Pagination cursor from previous response |
|
||||
| `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 | ISO 8601 timestamp of the operation |
|
||||
| `tasks` | array | Array of Confluence tasks |
|
||||
| ↳ `id` | string | Task ID |
|
||||
| ↳ `localId` | string | Local task ID |
|
||||
| ↳ `spaceId` | string | Space ID |
|
||||
| ↳ `pageId` | string | Page ID |
|
||||
| ↳ `blogPostId` | string | Blog post ID |
|
||||
| ↳ `status` | string | Task status \(complete or incomplete\) |
|
||||
| ↳ `body` | string | Task body content in storage format |
|
||||
| ↳ `createdBy` | string | Creator account ID |
|
||||
| ↳ `assignedTo` | string | Assignee account ID |
|
||||
| ↳ `completedBy` | string | Completer account ID |
|
||||
| ↳ `createdAt` | string | Creation timestamp |
|
||||
| ↳ `updatedAt` | string | Last update timestamp |
|
||||
| ↳ `dueAt` | string | Due date |
|
||||
| ↳ `completedAt` | string | Completion timestamp |
|
||||
| `nextCursor` | string | Cursor for fetching the next page of results |
|
||||
|
||||
### `confluence_get_task`
|
||||
|
||||
Get a specific Confluence inline task by ID.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Confluence domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `taskId` | string | Yes | The ID of the task to retrieve |
|
||||
| `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 | ISO 8601 timestamp of the operation |
|
||||
| `id` | string | Task ID |
|
||||
| `localId` | string | Local task ID |
|
||||
| `spaceId` | string | Space ID |
|
||||
| `pageId` | string | Page ID |
|
||||
| `blogPostId` | string | Blog post ID |
|
||||
| `status` | string | Task status \(complete or incomplete\) |
|
||||
| `body` | string | Task body content in storage format |
|
||||
| `createdBy` | string | Creator account ID |
|
||||
| `assignedTo` | string | Assignee account ID |
|
||||
| `completedBy` | string | Completer account ID |
|
||||
| `createdAt` | string | Creation timestamp |
|
||||
| `updatedAt` | string | Last update timestamp |
|
||||
| `dueAt` | string | Due date |
|
||||
| `completedAt` | string | Completion timestamp |
|
||||
|
||||
### `confluence_update_task`
|
||||
|
||||
Update the status of a Confluence inline task (complete or incomplete).
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Confluence domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `taskId` | string | Yes | The ID of the task to update |
|
||||
| `status` | string | Yes | New status for the task \(complete or incomplete\) |
|
||||
| `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 | ISO 8601 timestamp of the operation |
|
||||
| `id` | string | Task ID |
|
||||
| `localId` | string | Local task ID |
|
||||
| `spaceId` | string | Space ID |
|
||||
| `pageId` | string | Page ID |
|
||||
| `blogPostId` | string | Blog post ID |
|
||||
| `status` | string | Updated task status |
|
||||
| `body` | string | Task body content in storage format |
|
||||
| `createdBy` | string | Creator account ID |
|
||||
| `assignedTo` | string | Assignee account ID |
|
||||
| `completedBy` | string | Completer account ID |
|
||||
| `createdAt` | string | Creation timestamp |
|
||||
| `updatedAt` | string | Last update timestamp |
|
||||
| `dueAt` | string | Due date |
|
||||
| `completedAt` | string | Completion timestamp |
|
||||
|
||||
### `confluence_update_blogpost`
|
||||
|
||||
Update an existing Confluence blog post title and/or content.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Confluence domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `blogPostId` | string | Yes | The ID of the blog post to update |
|
||||
| `title` | string | No | New title for the blog post |
|
||||
| `content` | string | No | New content for the blog post in storage format |
|
||||
| `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 | ISO 8601 timestamp of the operation |
|
||||
| `blogPostId` | string | Updated blog post ID |
|
||||
| `title` | string | Blog post title |
|
||||
| `status` | string | Blog post status |
|
||||
| `spaceId` | string | Space ID |
|
||||
| `version` | json | Version information |
|
||||
| `url` | string | URL to view the blog post |
|
||||
|
||||
### `confluence_delete_blogpost`
|
||||
|
||||
Delete a Confluence blog post.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Confluence domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `blogPostId` | string | Yes | The ID of the blog post to delete |
|
||||
| `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 | ISO 8601 timestamp of the operation |
|
||||
| `blogPostId` | string | Deleted blog post ID |
|
||||
| `deleted` | boolean | Deletion status |
|
||||
|
||||
### `confluence_get_user`
|
||||
|
||||
Get display name and profile info for a Confluence user by account ID.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Confluence domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `accountId` | string | Yes | The Atlassian account ID of the user to look up |
|
||||
| `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 | ISO 8601 timestamp of the operation |
|
||||
| `accountId` | string | Atlassian account ID of the user |
|
||||
| `displayName` | string | Display name of the user |
|
||||
| `email` | string | Email address of the user |
|
||||
| `accountType` | string | Account type \(e.g., atlassian, app, customer\) |
|
||||
| `profilePicture` | string | Path to the user profile picture |
|
||||
| `publicName` | string | Public name of the user |
|
||||
|
||||
|
||||
|
||||
157
apps/docs/content/docs/en/tools/devin.mdx
Normal file
157
apps/docs/content/docs/en/tools/devin.mdx
Normal file
@@ -0,0 +1,157 @@
|
||||
---
|
||||
title: Devin
|
||||
description: Autonomous AI software engineer
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="devin"
|
||||
color="#12141A"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[Devin](https://devin.ai/) is an autonomous AI software engineer by Cognition that can independently write, run, debug, and deploy code.
|
||||
|
||||
With Devin, you can:
|
||||
|
||||
- **Automate coding tasks**: Assign software engineering tasks and let Devin autonomously write, test, and iterate on code
|
||||
- **Manage sessions**: Create, monitor, and interact with Devin sessions to track progress on assigned tasks
|
||||
- **Guide active work**: Send messages to running sessions to provide additional context, redirect efforts, or answer questions
|
||||
- **Retrieve structured output**: Poll completed sessions for pull requests, structured results, and detailed status
|
||||
- **Control costs**: Set ACU (Autonomous Compute Unit) limits to cap spending on long-running tasks
|
||||
- **Standardize workflows**: Use playbook IDs to apply repeatable task patterns across sessions
|
||||
|
||||
In Sim, the Devin integration enables your agents to programmatically manage Devin sessions as part of their workflows:
|
||||
|
||||
- **Create sessions**: Kick off new Devin sessions with a prompt describing the task, optional playbook, ACU limits, and tags
|
||||
- **Get session details**: Retrieve the full state of a session including status, pull requests, structured output, and resource consumption
|
||||
- **List sessions**: Query all sessions in your organization with optional pagination
|
||||
- **Send messages**: Communicate with active or suspended sessions to provide guidance, and automatically resume suspended sessions
|
||||
|
||||
This allows for powerful automation scenarios such as triggering code generation from upstream events, polling for completion before consuming results, orchestrating multi-step development pipelines, and integrating Devin's output into broader agent workflows.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Devin into your workflow. Create sessions to assign coding tasks, send messages to guide active sessions, and retrieve session status and results. Devin autonomously writes, runs, and tests code.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `devin_create_session`
|
||||
|
||||
Create a new Devin session with a prompt. Devin will autonomously work on the task described in the prompt.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Devin API key \(service user credential starting with cog_\) |
|
||||
| `prompt` | string | Yes | The task prompt for Devin to work on |
|
||||
| `playbookId` | string | No | Optional playbook ID to guide the session |
|
||||
| `maxAcuLimit` | number | No | Maximum ACU limit for the session |
|
||||
| `tags` | string | No | Comma-separated tags for the session |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `sessionId` | string | Unique identifier for the session |
|
||||
| `url` | string | URL to view the session in the Devin UI |
|
||||
| `status` | string | Session status \(new, claimed, running, exit, error, suspended, resuming\) |
|
||||
| `statusDetail` | string | Detailed status \(working, waiting_for_user, waiting_for_approval, finished, inactivity, etc.\) |
|
||||
| `title` | string | Session title |
|
||||
| `createdAt` | number | Unix timestamp when the session was created |
|
||||
| `updatedAt` | number | Unix timestamp when the session was last updated |
|
||||
| `acusConsumed` | number | ACUs consumed by the session |
|
||||
| `tags` | json | Tags associated with the session |
|
||||
| `pullRequests` | json | Pull requests created during the session |
|
||||
| `structuredOutput` | json | Structured output from the session |
|
||||
| `playbookId` | string | Associated playbook ID |
|
||||
|
||||
### `devin_get_session`
|
||||
|
||||
Retrieve details of an existing Devin session including status, tags, pull requests, and structured output.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Devin API key \(service user credential starting with cog_\) |
|
||||
| `sessionId` | string | Yes | The session ID to retrieve |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `sessionId` | string | Unique identifier for the session |
|
||||
| `url` | string | URL to view the session in the Devin UI |
|
||||
| `status` | string | Session status \(new, claimed, running, exit, error, suspended, resuming\) |
|
||||
| `statusDetail` | string | Detailed status \(working, waiting_for_user, waiting_for_approval, finished, inactivity, etc.\) |
|
||||
| `title` | string | Session title |
|
||||
| `createdAt` | number | Unix timestamp when the session was created |
|
||||
| `updatedAt` | number | Unix timestamp when the session was last updated |
|
||||
| `acusConsumed` | number | ACUs consumed by the session |
|
||||
| `tags` | json | Tags associated with the session |
|
||||
| `pullRequests` | json | Pull requests created during the session |
|
||||
| `structuredOutput` | json | Structured output from the session |
|
||||
| `playbookId` | string | Associated playbook ID |
|
||||
|
||||
### `devin_list_sessions`
|
||||
|
||||
List Devin sessions in the organization. Returns up to 100 sessions by default.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Devin API key \(service user credential starting with cog_\) |
|
||||
| `limit` | number | No | Maximum number of sessions to return \(1-200, default: 100\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `sessions` | array | List of Devin sessions |
|
||||
| ↳ `sessionId` | string | Unique identifier for the session |
|
||||
| ↳ `url` | string | URL to view the session |
|
||||
| ↳ `status` | string | Session status |
|
||||
| ↳ `statusDetail` | string | Detailed status |
|
||||
| ↳ `title` | string | Session title |
|
||||
| ↳ `createdAt` | number | Creation timestamp \(Unix\) |
|
||||
| ↳ `updatedAt` | number | Last updated timestamp \(Unix\) |
|
||||
| ↳ `tags` | json | Session tags |
|
||||
|
||||
### `devin_send_message`
|
||||
|
||||
Send a message to a Devin session. If the session is suspended, it will be automatically resumed. Returns the updated session state.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Devin API key \(service user credential starting with cog_\) |
|
||||
| `sessionId` | string | Yes | The session ID to send the message to |
|
||||
| `message` | string | Yes | The message to send to Devin |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `sessionId` | string | Unique identifier for the session |
|
||||
| `url` | string | URL to view the session in the Devin UI |
|
||||
| `status` | string | Session status \(new, claimed, running, exit, error, suspended, resuming\) |
|
||||
| `statusDetail` | string | Detailed status \(working, waiting_for_user, waiting_for_approval, finished, inactivity, etc.\) |
|
||||
| `title` | string | Session title |
|
||||
| `createdAt` | number | Unix timestamp when the session was created |
|
||||
| `updatedAt` | number | Unix timestamp when the session was last updated |
|
||||
| `acusConsumed` | number | ACUs consumed by the session |
|
||||
| `tags` | json | Tags associated with the session |
|
||||
| `pullRequests` | json | Pull requests created during the session |
|
||||
| `structuredOutput` | json | Structured output from the session |
|
||||
| `playbookId` | string | Associated playbook ID |
|
||||
|
||||
|
||||
168
apps/docs/content/docs/en/tools/google_bigquery.mdx
Normal file
168
apps/docs/content/docs/en/tools/google_bigquery.mdx
Normal file
@@ -0,0 +1,168 @@
|
||||
---
|
||||
title: Google BigQuery
|
||||
description: Query, list, and insert data in Google BigQuery
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="google_bigquery"
|
||||
color="#E0E0E0"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[Google BigQuery](https://cloud.google.com/bigquery) is Google Cloud's fully managed, serverless data warehouse designed for large-scale data analytics. BigQuery lets you run fast SQL queries on massive datasets, making it ideal for business intelligence, data exploration, and machine learning pipelines. It supports standard SQL, streaming inserts, and integrates with the broader Google Cloud ecosystem.
|
||||
|
||||
In Sim, the Google BigQuery integration allows your agents to query datasets, list tables, inspect schemas, and insert rows as part of automated workflows. This enables use cases such as automated reporting, data pipeline orchestration, real-time data ingestion, and analytics-driven decision making. By connecting Sim with BigQuery, your agents can pull insights from petabytes of data, write results back to tables, and keep your analytics workflows running without manual intervention.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Connect to Google BigQuery to run SQL queries, list datasets and tables, get table metadata, and insert rows.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `google_bigquery_query`
|
||||
|
||||
Run a SQL query against Google BigQuery and return the results
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Google Cloud project ID |
|
||||
| `query` | string | Yes | SQL query to execute |
|
||||
| `useLegacySql` | boolean | No | Whether to use legacy SQL syntax \(default: false\) |
|
||||
| `maxResults` | number | No | Maximum number of rows to return |
|
||||
| `defaultDatasetId` | string | No | Default dataset for unqualified table names |
|
||||
| `location` | string | No | Processing location \(e.g., "US", "EU"\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `columns` | array | Array of column names from the query result |
|
||||
| `rows` | array | Array of row objects keyed by column name |
|
||||
| `totalRows` | string | Total number of rows in the complete result set |
|
||||
| `jobComplete` | boolean | Whether the query completed within the timeout |
|
||||
| `totalBytesProcessed` | string | Total bytes processed by the query |
|
||||
| `cacheHit` | boolean | Whether the query result was served from cache |
|
||||
| `jobReference` | object | Job reference \(useful when jobComplete is false\) |
|
||||
| ↳ `projectId` | string | Project ID containing the job |
|
||||
| ↳ `jobId` | string | Unique job identifier |
|
||||
| ↳ `location` | string | Geographic location of the job |
|
||||
| `pageToken` | string | Token for fetching additional result pages |
|
||||
|
||||
### `google_bigquery_list_datasets`
|
||||
|
||||
List all datasets in a Google BigQuery project
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Google Cloud project ID |
|
||||
| `maxResults` | number | No | Maximum number of datasets to return |
|
||||
| `pageToken` | string | No | Token for pagination |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `datasets` | array | Array of dataset objects |
|
||||
| ↳ `datasetId` | string | Unique dataset identifier |
|
||||
| ↳ `projectId` | string | Project ID containing this dataset |
|
||||
| ↳ `friendlyName` | string | Descriptive name for the dataset |
|
||||
| ↳ `location` | string | Geographic location where the data resides |
|
||||
| `nextPageToken` | string | Token for fetching next page of results |
|
||||
|
||||
### `google_bigquery_list_tables`
|
||||
|
||||
List all tables in a Google BigQuery dataset
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Google Cloud project ID |
|
||||
| `datasetId` | string | Yes | BigQuery dataset ID |
|
||||
| `maxResults` | number | No | Maximum number of tables to return |
|
||||
| `pageToken` | string | No | Token for pagination |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `tables` | array | Array of table objects |
|
||||
| ↳ `tableId` | string | Table identifier |
|
||||
| ↳ `datasetId` | string | Dataset ID containing this table |
|
||||
| ↳ `projectId` | string | Project ID containing this table |
|
||||
| ↳ `type` | string | Table type \(TABLE, VIEW, EXTERNAL, etc.\) |
|
||||
| ↳ `friendlyName` | string | User-friendly name for the table |
|
||||
| ↳ `creationTime` | string | Time when created, in milliseconds since epoch |
|
||||
| `totalItems` | number | Total number of tables in the dataset |
|
||||
| `nextPageToken` | string | Token for fetching next page of results |
|
||||
|
||||
### `google_bigquery_get_table`
|
||||
|
||||
Get metadata and schema for a Google BigQuery table
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Google Cloud project ID |
|
||||
| `datasetId` | string | Yes | BigQuery dataset ID |
|
||||
| `tableId` | string | Yes | BigQuery table ID |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `tableId` | string | Table ID |
|
||||
| `datasetId` | string | Dataset ID |
|
||||
| `projectId` | string | Project ID |
|
||||
| `type` | string | Table type \(TABLE, VIEW, SNAPSHOT, MATERIALIZED_VIEW, EXTERNAL\) |
|
||||
| `description` | string | Table description |
|
||||
| `numRows` | string | Total number of rows |
|
||||
| `numBytes` | string | Total size in bytes, excluding data in streaming buffer |
|
||||
| `schema` | array | Array of column definitions |
|
||||
| ↳ `name` | string | Column name |
|
||||
| ↳ `type` | string | Data type \(STRING, INTEGER, FLOAT, BOOLEAN, TIMESTAMP, RECORD, etc.\) |
|
||||
| ↳ `mode` | string | Column mode \(NULLABLE, REQUIRED, or REPEATED\) |
|
||||
| ↳ `description` | string | Column description |
|
||||
| `creationTime` | string | Table creation time \(milliseconds since epoch\) |
|
||||
| `lastModifiedTime` | string | Last modification time \(milliseconds since epoch\) |
|
||||
| `location` | string | Geographic location where the table resides |
|
||||
|
||||
### `google_bigquery_insert_rows`
|
||||
|
||||
Insert rows into a Google BigQuery table using streaming insert
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Google Cloud project ID |
|
||||
| `datasetId` | string | Yes | BigQuery dataset ID |
|
||||
| `tableId` | string | Yes | BigQuery table ID |
|
||||
| `rows` | string | Yes | JSON array of row objects to insert |
|
||||
| `skipInvalidRows` | boolean | No | Whether to insert valid rows even if some are invalid |
|
||||
| `ignoreUnknownValues` | boolean | No | Whether to ignore columns not in the table schema |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `insertedRows` | number | Number of rows successfully inserted |
|
||||
| `errors` | array | Array of per-row insertion errors \(empty if all succeeded\) |
|
||||
| ↳ `index` | number | Zero-based index of the row that failed |
|
||||
| ↳ `errors` | array | Error details for this row |
|
||||
| ↳ `reason` | string | Short error code summarizing the error |
|
||||
| ↳ `location` | string | Where the error occurred |
|
||||
| ↳ `message` | string | Human-readable error description |
|
||||
|
||||
|
||||
@@ -10,6 +10,13 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
color="#E0E0E0"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[Google Books](https://books.google.com) is Google's comprehensive book discovery and metadata service, providing access to millions of books from publishers, libraries, and digitized collections worldwide. The Google Books API enables programmatic search and retrieval of detailed book information including titles, authors, descriptions, ratings, and publication details.
|
||||
|
||||
In Sim, the Google Books integration allows your agents to search for books and retrieve volume details as part of automated workflows. This enables use cases such as content research, reading list curation, bibliographic data enrichment, ISBN lookups, and knowledge gathering from published works. By connecting Sim with Google Books, your agents can discover and analyze book metadata, filter by availability or format, and incorporate literary references into their outputs—all without manual research.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Search for books using the Google Books API. Find volumes by title, author, ISBN, or keywords, and retrieve detailed information about specific books including descriptions, ratings, and publication details.
|
||||
|
||||
205
apps/docs/content/docs/en/tools/google_tasks.mdx
Normal file
205
apps/docs/content/docs/en/tools/google_tasks.mdx
Normal file
@@ -0,0 +1,205 @@
|
||||
---
|
||||
title: Google Tasks
|
||||
description: Manage Google Tasks
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="google_tasks"
|
||||
color="#E0E0E0"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[Google Tasks](https://support.google.com/tasks) is Google's lightweight task management service, integrated into Gmail, Google Calendar, and the standalone Google Tasks app. It provides a simple way to create, organize, and track to-do items with support for due dates, subtasks, and task lists. As part of Google Workspace, Google Tasks keeps your action items synchronized across all your devices.
|
||||
|
||||
In Sim, the Google Tasks integration allows your agents to create, read, update, delete, and list tasks and task lists as part of automated workflows. This enables use cases such as automated task creation from incoming data, to-do list management based on workflow triggers, task status tracking, and deadline monitoring. By connecting Sim with Google Tasks, your agents can manage action items programmatically, keep teams organized, and ensure nothing falls through the cracks.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Google Tasks into your workflow. Create, read, update, delete, and list tasks and task lists.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `google_tasks_create`
|
||||
|
||||
Create a new task in a Google Tasks list
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `taskListId` | string | No | Task list ID \(defaults to primary task list "@default"\) |
|
||||
| `title` | string | Yes | Title of the task \(max 1024 characters\) |
|
||||
| `notes` | string | No | Notes/description for the task \(max 8192 characters\) |
|
||||
| `due` | string | No | Due date in RFC 3339 format \(e.g., 2025-06-03T00:00:00.000Z\) |
|
||||
| `status` | string | No | Task status: "needsAction" or "completed" |
|
||||
| `parent` | string | No | Parent task ID to create this task as a subtask. Omit for top-level tasks. |
|
||||
| `previous` | string | No | Previous sibling task ID to position after. Omit to place first among siblings. |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Task ID |
|
||||
| `title` | string | Task title |
|
||||
| `notes` | string | Task notes |
|
||||
| `status` | string | Task status \(needsAction or completed\) |
|
||||
| `due` | string | Due date |
|
||||
| `updated` | string | Last modification time |
|
||||
| `selfLink` | string | URL for the task |
|
||||
| `webViewLink` | string | Link to task in Google Tasks UI |
|
||||
| `parent` | string | Parent task ID |
|
||||
| `position` | string | Position among sibling tasks |
|
||||
| `completed` | string | Completion date |
|
||||
| `deleted` | boolean | Whether the task is deleted |
|
||||
|
||||
### `google_tasks_list`
|
||||
|
||||
List all tasks in a Google Tasks list
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `taskListId` | string | No | Task list ID \(defaults to primary task list "@default"\) |
|
||||
| `maxResults` | number | No | Maximum number of tasks to return \(default 20, max 100\) |
|
||||
| `pageToken` | string | No | Token for pagination |
|
||||
| `showCompleted` | boolean | No | Whether to show completed tasks \(default true\) |
|
||||
| `showDeleted` | boolean | No | Whether to show deleted tasks \(default false\) |
|
||||
| `showHidden` | boolean | No | Whether to show hidden tasks \(default false\) |
|
||||
| `dueMin` | string | No | Lower bound for due date filter \(RFC 3339 timestamp\) |
|
||||
| `dueMax` | string | No | Upper bound for due date filter \(RFC 3339 timestamp\) |
|
||||
| `completedMin` | string | No | Lower bound for task completion date \(RFC 3339 timestamp\) |
|
||||
| `completedMax` | string | No | Upper bound for task completion date \(RFC 3339 timestamp\) |
|
||||
| `updatedMin` | string | No | Lower bound for last modification time \(RFC 3339 timestamp\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `tasks` | array | List of tasks |
|
||||
| ↳ `id` | string | Task identifier |
|
||||
| ↳ `title` | string | Title of the task |
|
||||
| ↳ `notes` | string | Notes/description for the task |
|
||||
| ↳ `status` | string | Task status: "needsAction" or "completed" |
|
||||
| ↳ `due` | string | Due date \(RFC 3339 timestamp\) |
|
||||
| ↳ `completed` | string | Completion date \(RFC 3339 timestamp\) |
|
||||
| ↳ `updated` | string | Last modification time \(RFC 3339 timestamp\) |
|
||||
| ↳ `selfLink` | string | URL pointing to this task |
|
||||
| ↳ `webViewLink` | string | Link to task in Google Tasks UI |
|
||||
| ↳ `parent` | string | Parent task identifier |
|
||||
| ↳ `position` | string | Position among sibling tasks \(string-based ordering\) |
|
||||
| ↳ `hidden` | boolean | Whether the task is hidden |
|
||||
| ↳ `deleted` | boolean | Whether the task is deleted |
|
||||
| ↳ `links` | array | Collection of links associated with the task |
|
||||
| ↳ `type` | string | Link type \(e.g., "email", "generic", "chat_message"\) |
|
||||
| ↳ `description` | string | Link description |
|
||||
| ↳ `link` | string | The URL |
|
||||
| `nextPageToken` | string | Token for retrieving the next page of results |
|
||||
|
||||
### `google_tasks_get`
|
||||
|
||||
Retrieve a specific task by ID from a Google Tasks list
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `taskListId` | string | No | Task list ID \(defaults to primary task list "@default"\) |
|
||||
| `taskId` | string | Yes | The ID of the task to retrieve |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Task ID |
|
||||
| `title` | string | Task title |
|
||||
| `notes` | string | Task notes |
|
||||
| `status` | string | Task status \(needsAction or completed\) |
|
||||
| `due` | string | Due date |
|
||||
| `updated` | string | Last modification time |
|
||||
| `selfLink` | string | URL for the task |
|
||||
| `webViewLink` | string | Link to task in Google Tasks UI |
|
||||
| `parent` | string | Parent task ID |
|
||||
| `position` | string | Position among sibling tasks |
|
||||
| `completed` | string | Completion date |
|
||||
| `deleted` | boolean | Whether the task is deleted |
|
||||
|
||||
### `google_tasks_update`
|
||||
|
||||
Update an existing task in a Google Tasks list
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `taskListId` | string | No | Task list ID \(defaults to primary task list "@default"\) |
|
||||
| `taskId` | string | Yes | The ID of the task to update |
|
||||
| `title` | string | No | New title for the task |
|
||||
| `notes` | string | No | New notes for the task |
|
||||
| `due` | string | No | New due date in RFC 3339 format |
|
||||
| `status` | string | No | New status: "needsAction" or "completed" |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Task ID |
|
||||
| `title` | string | Task title |
|
||||
| `notes` | string | Task notes |
|
||||
| `status` | string | Task status \(needsAction or completed\) |
|
||||
| `due` | string | Due date |
|
||||
| `updated` | string | Last modification time |
|
||||
| `selfLink` | string | URL for the task |
|
||||
| `webViewLink` | string | Link to task in Google Tasks UI |
|
||||
| `parent` | string | Parent task ID |
|
||||
| `position` | string | Position among sibling tasks |
|
||||
| `completed` | string | Completion date |
|
||||
| `deleted` | boolean | Whether the task is deleted |
|
||||
|
||||
### `google_tasks_delete`
|
||||
|
||||
Delete a task from a Google Tasks list
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `taskListId` | string | No | Task list ID \(defaults to primary task list "@default"\) |
|
||||
| `taskId` | string | Yes | The ID of the task to delete |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `taskId` | string | Deleted task ID |
|
||||
| `deleted` | boolean | Whether deletion was successful |
|
||||
|
||||
### `google_tasks_list_task_lists`
|
||||
|
||||
Retrieve all task lists for the authenticated user
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `maxResults` | number | No | Maximum number of task lists to return \(default 20, max 100\) |
|
||||
| `pageToken` | string | No | Token for pagination |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `taskLists` | array | List of task lists |
|
||||
| ↳ `id` | string | Task list identifier |
|
||||
| ↳ `title` | string | Title of the task list |
|
||||
| ↳ `updated` | string | Last modification time \(RFC 3339 timestamp\) |
|
||||
| ↳ `selfLink` | string | URL pointing to this task list |
|
||||
| `nextPageToken` | string | Token for retrieving the next page of results |
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"confluence",
|
||||
"cursor",
|
||||
"datadog",
|
||||
"devin",
|
||||
"discord",
|
||||
"dropbox",
|
||||
"dspy",
|
||||
@@ -37,6 +38,7 @@
|
||||
"gitlab",
|
||||
"gmail",
|
||||
"gong",
|
||||
"google_bigquery",
|
||||
"google_books",
|
||||
"google_calendar",
|
||||
"google_docs",
|
||||
@@ -47,6 +49,7 @@
|
||||
"google_search",
|
||||
"google_sheets",
|
||||
"google_slides",
|
||||
"google_tasks",
|
||||
"google_translate",
|
||||
"google_vault",
|
||||
"grafana",
|
||||
|
||||
@@ -5,11 +5,12 @@ description: User-defined data tables for storing and querying structured data
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
<BlockInfoCard
|
||||
type="table"
|
||||
color="#10B981"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
Tables allow you to create and manage custom data tables directly within Sim. Store, query, and manipulate structured data within your workflows without needing external database integrations.
|
||||
|
||||
**Why Use Tables?**
|
||||
@@ -26,6 +27,7 @@ Tables allow you to create and manage custom data tables directly within Sim. St
|
||||
- Batch operations for bulk inserts
|
||||
- Bulk updates and deletes by filter
|
||||
- Up to 10,000 rows per table, 100 tables per workspace
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
## Creating Tables
|
||||
|
||||
|
||||
@@ -283,3 +283,165 @@ export async function POST(request: NextRequest) {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a blog post
|
||||
*/
|
||||
export async function PUT(request: NextRequest) {
|
||||
try {
|
||||
const auth = await checkSessionOrInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
const body = await request.json()
|
||||
const { domain, accessToken, blogPostId, title, content, cloudId: providedCloudId } = body
|
||||
|
||||
if (!domain || !accessToken || !blogPostId) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Domain, access token, and blog post ID are required' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const blogPostIdValidation = validateAlphanumericId(blogPostId, 'blogPostId', 255)
|
||||
if (!blogPostIdValidation.isValid) {
|
||||
return NextResponse.json({ error: blogPostIdValidation.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 })
|
||||
}
|
||||
|
||||
// Fetch current blog post to get version number
|
||||
const currentUrl = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/api/v2/blogposts/${blogPostId}?body-format=storage`
|
||||
const currentResponse = await fetch(currentUrl, {
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (!currentResponse.ok) {
|
||||
throw new Error(`Failed to fetch current blog post: ${currentResponse.status}`)
|
||||
}
|
||||
|
||||
const currentPost = await currentResponse.json()
|
||||
|
||||
if (!currentPost.version?.number) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Unable to determine current blog post version' },
|
||||
{ status: 422 }
|
||||
)
|
||||
}
|
||||
|
||||
const currentVersion = currentPost.version.number
|
||||
|
||||
const updateBody: Record<string, unknown> = {
|
||||
id: blogPostId,
|
||||
version: { number: currentVersion + 1 },
|
||||
status: 'current',
|
||||
title: title || currentPost.title,
|
||||
body: {
|
||||
representation: 'storage',
|
||||
value: content || currentPost.body?.storage?.value || '',
|
||||
},
|
||||
}
|
||||
|
||||
const response = await fetch(currentUrl, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
body: JSON.stringify(updateBody),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
logger.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
})
|
||||
const errorMessage = errorData?.message || `Failed to update blog post (${response.status})`
|
||||
return NextResponse.json({ error: errorMessage }, { status: response.status })
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
return NextResponse.json(data)
|
||||
} catch (error) {
|
||||
logger.error('Error updating blog post:', error)
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message || 'Internal server error' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a blog post
|
||||
*/
|
||||
export async function DELETE(request: NextRequest) {
|
||||
try {
|
||||
const auth = await checkSessionOrInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
const body = await request.json()
|
||||
const { domain, accessToken, blogPostId, cloudId: providedCloudId } = body
|
||||
|
||||
if (!domain || !accessToken || !blogPostId) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Domain, access token, and blog post ID are required' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const blogPostIdValidation = validateAlphanumericId(blogPostId, 'blogPostId', 255)
|
||||
if (!blogPostIdValidation.isValid) {
|
||||
return NextResponse.json({ error: blogPostIdValidation.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 })
|
||||
}
|
||||
|
||||
const url = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/api/v2/blogposts/${blogPostId}`
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
logger.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
})
|
||||
const errorMessage = errorData?.message || `Failed to delete blog post (${response.status})`
|
||||
return NextResponse.json({ error: errorMessage }, { status: response.status })
|
||||
}
|
||||
|
||||
return NextResponse.json({ blogPostId, deleted: true })
|
||||
} catch (error) {
|
||||
logger.error('Error deleting blog post:', error)
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message || 'Internal server error' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
115
apps/sim/app/api/tools/confluence/page-descendants/route.ts
Normal file
115
apps/sim/app/api/tools/confluence/page-descendants/route.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import {
|
||||
validateAlphanumericId,
|
||||
validateJiraCloudId,
|
||||
validatePaginationCursor,
|
||||
} from '@/lib/core/security/input-validation'
|
||||
import { getConfluenceCloudId } from '@/tools/confluence/utils'
|
||||
|
||||
const logger = createLogger('ConfluencePageDescendantsAPI')
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
/**
|
||||
* Get all descendants of a Confluence page recursively.
|
||||
* Uses GET /wiki/api/v2/pages/{id}/descendants
|
||||
*/
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const auth = await checkSessionOrInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
const body = await request.json()
|
||||
const { domain, accessToken, pageId, cloudId: providedCloudId, limit = 50, cursor } = body
|
||||
|
||||
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 })
|
||||
}
|
||||
|
||||
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 })
|
||||
}
|
||||
|
||||
const queryParams = new URLSearchParams()
|
||||
queryParams.append('limit', String(Math.min(limit, 250)))
|
||||
|
||||
if (cursor) {
|
||||
const cursorValidation = validatePaginationCursor(cursor, 'cursor')
|
||||
if (!cursorValidation.isValid) {
|
||||
return NextResponse.json({ error: cursorValidation.error }, { status: 400 })
|
||||
}
|
||||
queryParams.append('cursor', cursor)
|
||||
}
|
||||
|
||||
const url = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/api/v2/pages/${pageId}/descendants?${queryParams.toString()}`
|
||||
|
||||
logger.info(`Fetching descendants for page ${pageId}`)
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
logger.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
})
|
||||
const errorMessage =
|
||||
errorData?.message || `Failed to get page descendants (${response.status})`
|
||||
return NextResponse.json({ error: errorMessage }, { status: response.status })
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
const descendants = (data.results || []).map((page: any) => ({
|
||||
id: page.id,
|
||||
title: page.title,
|
||||
type: page.type ?? null,
|
||||
status: page.status ?? null,
|
||||
spaceId: page.spaceId ?? null,
|
||||
parentId: page.parentId ?? null,
|
||||
childPosition: page.childPosition ?? null,
|
||||
depth: page.depth ?? null,
|
||||
}))
|
||||
|
||||
return NextResponse.json({
|
||||
descendants,
|
||||
pageId,
|
||||
nextCursor: data._links?.next
|
||||
? new URL(data._links.next, 'https://placeholder').searchParams.get('cursor')
|
||||
: null,
|
||||
})
|
||||
} catch (error) {
|
||||
logger.error('Error getting page descendants:', error)
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message || 'Internal server error' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,12 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
|
||||
import {
|
||||
validateAlphanumericId,
|
||||
validateJiraCloudId,
|
||||
validateNumericId,
|
||||
validatePaginationCursor,
|
||||
} from '@/lib/core/security/input-validation'
|
||||
import { cleanHtmlContent, getConfluenceCloudId } from '@/tools/confluence/utils'
|
||||
|
||||
const logger = createLogger('ConfluencePageVersionsAPI')
|
||||
@@ -57,8 +62,14 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
// If versionNumber is provided, get specific version with page content
|
||||
if (versionNumber !== undefined && versionNumber !== null) {
|
||||
const versionUrl = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/api/v2/pages/${pageId}/versions/${versionNumber}`
|
||||
const pageUrl = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/api/v2/pages/${pageId}?version=${versionNumber}&body-format=storage`
|
||||
const versionValidation = validateNumericId(versionNumber, 'versionNumber', { min: 1 })
|
||||
if (!versionValidation.isValid) {
|
||||
return NextResponse.json({ error: versionValidation.error }, { status: 400 })
|
||||
}
|
||||
const safeVersion = versionValidation.sanitized
|
||||
|
||||
const versionUrl = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/api/v2/pages/${pageId}/versions/${safeVersion}`
|
||||
const pageUrl = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/api/v2/pages/${pageId}?version=${safeVersion}&body-format=storage`
|
||||
|
||||
logger.info(`Fetching version ${versionNumber} for page ${pageId}`)
|
||||
|
||||
@@ -135,6 +146,10 @@ export async function POST(request: NextRequest) {
|
||||
queryParams.append('limit', String(Math.min(limit, 250)))
|
||||
|
||||
if (cursor) {
|
||||
const cursorValidation = validatePaginationCursor(cursor, 'cursor')
|
||||
if (!cursorValidation.isValid) {
|
||||
return NextResponse.json({ error: cursorValidation.error }, { status: 400 })
|
||||
}
|
||||
queryParams.append('cursor', cursor)
|
||||
}
|
||||
|
||||
|
||||
@@ -185,7 +185,7 @@ export async function PUT(request: NextRequest) {
|
||||
return NextResponse.json({ error: cloudIdValidation.error }, { status: 400 })
|
||||
}
|
||||
|
||||
const currentPageUrl = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/api/v2/pages/${pageId}`
|
||||
const currentPageUrl = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/api/v2/pages/${pageId}?body-format=storage`
|
||||
const currentPageResponse = await fetch(currentPageUrl, {
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
|
||||
114
apps/sim/app/api/tools/confluence/space-permissions/route.ts
Normal file
114
apps/sim/app/api/tools/confluence/space-permissions/route.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import {
|
||||
validateAlphanumericId,
|
||||
validateJiraCloudId,
|
||||
validatePaginationCursor,
|
||||
} from '@/lib/core/security/input-validation'
|
||||
import { getConfluenceCloudId } from '@/tools/confluence/utils'
|
||||
|
||||
const logger = createLogger('ConfluenceSpacePermissionsAPI')
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
/**
|
||||
* List permissions for a Confluence space.
|
||||
* Uses GET /wiki/api/v2/spaces/{id}/permissions
|
||||
*/
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const auth = await checkSessionOrInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
const body = await request.json()
|
||||
const { domain, accessToken, spaceId, cloudId: providedCloudId, limit = 50, cursor } = body
|
||||
|
||||
if (!domain) {
|
||||
return NextResponse.json({ error: 'Domain is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
if (!accessToken) {
|
||||
return NextResponse.json({ error: 'Access token is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
if (!spaceId) {
|
||||
return NextResponse.json({ error: 'Space ID is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
const spaceIdValidation = validateAlphanumericId(spaceId, 'spaceId', 255)
|
||||
if (!spaceIdValidation.isValid) {
|
||||
return NextResponse.json({ error: spaceIdValidation.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 })
|
||||
}
|
||||
|
||||
const queryParams = new URLSearchParams()
|
||||
queryParams.append('limit', String(Math.min(limit, 250)))
|
||||
|
||||
if (cursor) {
|
||||
const cursorValidation = validatePaginationCursor(cursor, 'cursor')
|
||||
if (!cursorValidation.isValid) {
|
||||
return NextResponse.json({ error: cursorValidation.error }, { status: 400 })
|
||||
}
|
||||
queryParams.append('cursor', cursor)
|
||||
}
|
||||
|
||||
const url = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/api/v2/spaces/${spaceId}/permissions?${queryParams.toString()}`
|
||||
|
||||
logger.info(`Fetching permissions for space ${spaceId}`)
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
logger.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
})
|
||||
const errorMessage =
|
||||
errorData?.message || `Failed to list space permissions (${response.status})`
|
||||
return NextResponse.json({ error: errorMessage }, { status: response.status })
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
const permissions = (data.results || []).map((perm: any) => ({
|
||||
id: perm.id,
|
||||
principalType: perm.principal?.type ?? null,
|
||||
principalId: perm.principal?.id ?? null,
|
||||
operationKey: perm.operation?.key ?? null,
|
||||
operationTargetType: perm.operation?.targetType ?? null,
|
||||
anonymousAccess: perm.anonymousAccess ?? false,
|
||||
unlicensedAccess: perm.unlicensedAccess ?? false,
|
||||
}))
|
||||
|
||||
return NextResponse.json({
|
||||
permissions,
|
||||
spaceId,
|
||||
nextCursor: data._links?.next
|
||||
? new URL(data._links.next, 'https://placeholder').searchParams.get('cursor')
|
||||
: null,
|
||||
})
|
||||
} catch (error) {
|
||||
logger.error('Error listing space permissions:', error)
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message || 'Internal server error' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
209
apps/sim/app/api/tools/confluence/space-properties/route.ts
Normal file
209
apps/sim/app/api/tools/confluence/space-properties/route.ts
Normal file
@@ -0,0 +1,209 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import {
|
||||
validateAlphanumericId,
|
||||
validateJiraCloudId,
|
||||
validatePaginationCursor,
|
||||
} from '@/lib/core/security/input-validation'
|
||||
import { getConfluenceCloudId } from '@/tools/confluence/utils'
|
||||
|
||||
const logger = createLogger('ConfluenceSpacePropertiesAPI')
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
/**
|
||||
* List, create, or delete space properties.
|
||||
* Uses GET/POST /wiki/api/v2/spaces/{id}/properties
|
||||
* and DELETE /wiki/api/v2/spaces/{id}/properties/{propertyId}
|
||||
*/
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const auth = await checkSessionOrInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
const body = await request.json()
|
||||
const {
|
||||
domain,
|
||||
accessToken,
|
||||
spaceId,
|
||||
cloudId: providedCloudId,
|
||||
action,
|
||||
key,
|
||||
value,
|
||||
propertyId,
|
||||
limit = 50,
|
||||
cursor,
|
||||
} = body
|
||||
|
||||
if (!domain) {
|
||||
return NextResponse.json({ error: 'Domain is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
if (!accessToken) {
|
||||
return NextResponse.json({ error: 'Access token is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
if (!spaceId) {
|
||||
return NextResponse.json({ error: 'Space ID is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
const spaceIdValidation = validateAlphanumericId(spaceId, 'spaceId', 255)
|
||||
if (!spaceIdValidation.isValid) {
|
||||
return NextResponse.json({ error: spaceIdValidation.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 })
|
||||
}
|
||||
|
||||
const baseUrl = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/api/v2/spaces/${spaceId}/properties`
|
||||
|
||||
// Validate required params for specific actions
|
||||
if (action === 'delete' && !propertyId) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Property ID is required for delete action' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
if (action === 'create' && !key) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Property key is required for create action' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// Delete a property
|
||||
if (action === 'delete' && propertyId) {
|
||||
const propertyIdValidation = validateAlphanumericId(propertyId, 'propertyId', 255)
|
||||
if (!propertyIdValidation.isValid) {
|
||||
return NextResponse.json({ error: propertyIdValidation.error }, { status: 400 })
|
||||
}
|
||||
|
||||
const url = `${baseUrl}/${encodeURIComponent(propertyId)}`
|
||||
|
||||
logger.info(`Deleting space property ${propertyId} from space ${spaceId}`)
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
logger.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
})
|
||||
const errorMessage =
|
||||
errorData?.message || `Failed to delete space property (${response.status})`
|
||||
return NextResponse.json({ error: errorMessage }, { status: response.status })
|
||||
}
|
||||
|
||||
return NextResponse.json({ spaceId, propertyId, deleted: true })
|
||||
}
|
||||
|
||||
// Create a property
|
||||
if (action === 'create' && key) {
|
||||
logger.info(`Creating space property '${key}' on space ${spaceId}`)
|
||||
|
||||
const response = await fetch(baseUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
body: JSON.stringify({ key, value: value ?? {} }),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
logger.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
})
|
||||
const errorMessage =
|
||||
errorData?.message || `Failed to create space property (${response.status})`
|
||||
return NextResponse.json({ error: errorMessage }, { status: response.status })
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
return NextResponse.json({
|
||||
propertyId: data.id,
|
||||
key: data.key,
|
||||
value: data.value ?? null,
|
||||
spaceId,
|
||||
})
|
||||
}
|
||||
|
||||
// List properties
|
||||
const queryParams = new URLSearchParams()
|
||||
queryParams.append('limit', String(Math.min(limit, 250)))
|
||||
|
||||
if (cursor) {
|
||||
const cursorValidation = validatePaginationCursor(cursor, 'cursor')
|
||||
if (!cursorValidation.isValid) {
|
||||
return NextResponse.json({ error: cursorValidation.error }, { status: 400 })
|
||||
}
|
||||
queryParams.append('cursor', cursor)
|
||||
}
|
||||
|
||||
const url = `${baseUrl}?${queryParams.toString()}`
|
||||
|
||||
logger.info(`Fetching properties for space ${spaceId}`)
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
logger.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
})
|
||||
const errorMessage =
|
||||
errorData?.message || `Failed to list space properties (${response.status})`
|
||||
return NextResponse.json({ error: errorMessage }, { status: response.status })
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
const properties = (data.results || []).map((prop: any) => ({
|
||||
id: prop.id,
|
||||
key: prop.key,
|
||||
value: prop.value ?? null,
|
||||
}))
|
||||
|
||||
return NextResponse.json({
|
||||
properties,
|
||||
spaceId,
|
||||
nextCursor: data._links?.next
|
||||
? new URL(data._links.next, 'https://placeholder').searchParams.get('cursor')
|
||||
: null,
|
||||
})
|
||||
} catch (error) {
|
||||
logger.error('Error with space properties:', error)
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message || 'Internal server error' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -78,3 +78,258 @@ export async function GET(request: NextRequest) {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Confluence space.
|
||||
* Uses POST /wiki/api/v2/spaces
|
||||
*/
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const auth = await checkSessionOrInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
const body = await request.json()
|
||||
const { domain, accessToken, name, key, description, cloudId: providedCloudId } = body
|
||||
|
||||
if (!domain) {
|
||||
return NextResponse.json({ error: 'Domain is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
if (!accessToken) {
|
||||
return NextResponse.json({ error: 'Access token is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
return NextResponse.json({ error: 'Space name is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
if (!key) {
|
||||
return NextResponse.json({ error: 'Space key is required' }, { 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 })
|
||||
}
|
||||
|
||||
const url = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/api/v2/spaces`
|
||||
|
||||
const createBody: Record<string, unknown> = { name, key }
|
||||
if (description) {
|
||||
createBody.description = { value: description, representation: 'plain' }
|
||||
}
|
||||
|
||||
logger.info(`Creating space with key ${key}`)
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
body: JSON.stringify(createBody),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
logger.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
})
|
||||
const errorMessage = errorData?.message || `Failed to create space (${response.status})`
|
||||
return NextResponse.json({ error: errorMessage }, { status: response.status })
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
return NextResponse.json(data)
|
||||
} catch (error) {
|
||||
logger.error('Error creating Confluence space:', error)
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message || 'Internal server error' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a Confluence space.
|
||||
* Uses PUT /wiki/api/v2/spaces/{id}
|
||||
*/
|
||||
export async function PUT(request: NextRequest) {
|
||||
try {
|
||||
const auth = await checkSessionOrInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
const body = await request.json()
|
||||
const { domain, accessToken, spaceId, name, description, cloudId: providedCloudId } = body
|
||||
|
||||
if (!domain) {
|
||||
return NextResponse.json({ error: 'Domain is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
if (!accessToken) {
|
||||
return NextResponse.json({ error: 'Access token is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
if (!spaceId) {
|
||||
return NextResponse.json({ error: 'Space ID is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
const spaceIdValidation = validateAlphanumericId(spaceId, 'spaceId', 255)
|
||||
if (!spaceIdValidation.isValid) {
|
||||
return NextResponse.json({ error: spaceIdValidation.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 })
|
||||
}
|
||||
|
||||
const url = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/api/v2/spaces/${spaceId}`
|
||||
|
||||
if (!name && description === undefined) {
|
||||
return NextResponse.json(
|
||||
{ error: 'At least one of name or description is required for update' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const updateBody: Record<string, unknown> = {}
|
||||
|
||||
if (name) {
|
||||
updateBody.name = name
|
||||
} else {
|
||||
const currentResponse = await fetch(url, {
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
})
|
||||
if (!currentResponse.ok) {
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to fetch current space: ${currentResponse.status}` },
|
||||
{ status: currentResponse.status }
|
||||
)
|
||||
}
|
||||
const currentSpace = await currentResponse.json()
|
||||
updateBody.name = currentSpace.name
|
||||
}
|
||||
|
||||
if (description !== undefined) {
|
||||
updateBody.description = { value: description, representation: 'plain' }
|
||||
}
|
||||
|
||||
logger.info(`Updating space ${spaceId}`)
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
body: JSON.stringify(updateBody),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
logger.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
})
|
||||
const errorMessage = errorData?.message || `Failed to update space (${response.status})`
|
||||
return NextResponse.json({ error: errorMessage }, { status: response.status })
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
return NextResponse.json(data)
|
||||
} catch (error) {
|
||||
logger.error('Error updating Confluence space:', error)
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message || 'Internal server error' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a Confluence space.
|
||||
* Uses DELETE /wiki/api/v2/spaces/{id}
|
||||
*/
|
||||
export async function DELETE(request: NextRequest) {
|
||||
try {
|
||||
const auth = await checkSessionOrInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
const body = await request.json()
|
||||
const { domain, accessToken, spaceId, cloudId: providedCloudId } = body
|
||||
|
||||
if (!domain) {
|
||||
return NextResponse.json({ error: 'Domain is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
if (!accessToken) {
|
||||
return NextResponse.json({ error: 'Access token is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
if (!spaceId) {
|
||||
return NextResponse.json({ error: 'Space ID is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
const spaceIdValidation = validateAlphanumericId(spaceId, 'spaceId', 255)
|
||||
if (!spaceIdValidation.isValid) {
|
||||
return NextResponse.json({ error: spaceIdValidation.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 })
|
||||
}
|
||||
|
||||
const url = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/api/v2/spaces/${spaceId}`
|
||||
|
||||
logger.info(`Deleting space ${spaceId}`)
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
logger.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
})
|
||||
const errorMessage = errorData?.message || `Failed to delete space (${response.status})`
|
||||
return NextResponse.json({ error: errorMessage }, { status: response.status })
|
||||
}
|
||||
|
||||
return NextResponse.json({ spaceId, deleted: true })
|
||||
} catch (error) {
|
||||
logger.error('Error deleting Confluence space:', error)
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message || 'Internal server error' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
278
apps/sim/app/api/tools/confluence/tasks/route.ts
Normal file
278
apps/sim/app/api/tools/confluence/tasks/route.ts
Normal file
@@ -0,0 +1,278 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import {
|
||||
validateAlphanumericId,
|
||||
validateJiraCloudId,
|
||||
validatePaginationCursor,
|
||||
validatePathSegment,
|
||||
} from '@/lib/core/security/input-validation'
|
||||
import { getConfluenceCloudId } from '@/tools/confluence/utils'
|
||||
|
||||
const logger = createLogger('ConfluenceTasksAPI')
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
/**
|
||||
* List, get, or update Confluence inline tasks.
|
||||
* Uses GET /wiki/api/v2/tasks, GET /wiki/api/v2/tasks/{id}, PUT /wiki/api/v2/tasks/{id}
|
||||
*/
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const auth = await checkSessionOrInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
const body = await request.json()
|
||||
const {
|
||||
domain,
|
||||
accessToken,
|
||||
cloudId: providedCloudId,
|
||||
action,
|
||||
taskId,
|
||||
status: taskStatus,
|
||||
pageId,
|
||||
spaceId,
|
||||
assignedTo,
|
||||
limit = 50,
|
||||
cursor,
|
||||
} = body
|
||||
|
||||
if (!domain) {
|
||||
return NextResponse.json({ error: 'Domain is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
if (!accessToken) {
|
||||
return NextResponse.json({ error: 'Access token is required' }, { 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 })
|
||||
}
|
||||
|
||||
// Update a task
|
||||
if (action === 'update' && taskId) {
|
||||
const taskIdValidation = validateAlphanumericId(taskId, 'taskId', 255)
|
||||
if (!taskIdValidation.isValid) {
|
||||
return NextResponse.json({ error: taskIdValidation.error }, { status: 400 })
|
||||
}
|
||||
|
||||
// First fetch the current task to get required fields
|
||||
const getUrl = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/api/v2/tasks/${taskId}`
|
||||
const getResponse = await fetch(getUrl, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (!getResponse.ok) {
|
||||
const errorData = await getResponse.json().catch(() => null)
|
||||
const errorMessage = errorData?.message || `Failed to fetch task (${getResponse.status})`
|
||||
return NextResponse.json({ error: errorMessage }, { status: getResponse.status })
|
||||
}
|
||||
|
||||
const currentTask = await getResponse.json()
|
||||
|
||||
const updateBody: Record<string, unknown> = {
|
||||
id: taskId,
|
||||
status: taskStatus || currentTask.status,
|
||||
}
|
||||
|
||||
const url = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/api/v2/tasks/${taskId}`
|
||||
|
||||
logger.info(`Updating task ${taskId}`)
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
body: JSON.stringify(updateBody),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
logger.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
})
|
||||
const errorMessage = errorData?.message || `Failed to update task (${response.status})`
|
||||
return NextResponse.json({ error: errorMessage }, { status: response.status })
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
return NextResponse.json({
|
||||
task: {
|
||||
id: data.id,
|
||||
localId: data.localId ?? null,
|
||||
spaceId: data.spaceId ?? null,
|
||||
pageId: data.pageId ?? null,
|
||||
blogPostId: data.blogPostId ?? null,
|
||||
status: data.status,
|
||||
body: data.body?.storage?.value ?? null,
|
||||
createdBy: data.createdBy ?? null,
|
||||
assignedTo: data.assignedTo ?? null,
|
||||
completedBy: data.completedBy ?? null,
|
||||
createdAt: data.createdAt ?? null,
|
||||
updatedAt: data.updatedAt ?? null,
|
||||
dueAt: data.dueAt ?? null,
|
||||
completedAt: data.completedAt ?? null,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Get a specific task
|
||||
if (taskId) {
|
||||
const taskIdValidation = validateAlphanumericId(taskId, 'taskId', 255)
|
||||
if (!taskIdValidation.isValid) {
|
||||
return NextResponse.json({ error: taskIdValidation.error }, { status: 400 })
|
||||
}
|
||||
|
||||
const url = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/api/v2/tasks/${taskId}`
|
||||
|
||||
logger.info(`Fetching task ${taskId}`)
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
logger.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
})
|
||||
const errorMessage = errorData?.message || `Failed to get task (${response.status})`
|
||||
return NextResponse.json({ error: errorMessage }, { status: response.status })
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
return NextResponse.json({
|
||||
task: {
|
||||
id: data.id,
|
||||
localId: data.localId ?? null,
|
||||
spaceId: data.spaceId ?? null,
|
||||
pageId: data.pageId ?? null,
|
||||
blogPostId: data.blogPostId ?? null,
|
||||
status: data.status,
|
||||
body: data.body?.storage?.value ?? null,
|
||||
createdBy: data.createdBy ?? null,
|
||||
assignedTo: data.assignedTo ?? null,
|
||||
completedBy: data.completedBy ?? null,
|
||||
createdAt: data.createdAt ?? null,
|
||||
updatedAt: data.updatedAt ?? null,
|
||||
dueAt: data.dueAt ?? null,
|
||||
completedAt: data.completedAt ?? null,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// List tasks
|
||||
const queryParams = new URLSearchParams()
|
||||
queryParams.append('limit', String(Math.min(limit, 250)))
|
||||
|
||||
if (cursor) {
|
||||
const cursorValidation = validatePaginationCursor(cursor, 'cursor')
|
||||
if (!cursorValidation.isValid) {
|
||||
return NextResponse.json({ error: cursorValidation.error }, { status: 400 })
|
||||
}
|
||||
queryParams.append('cursor', cursor)
|
||||
}
|
||||
if (taskStatus) queryParams.append('status', taskStatus)
|
||||
if (pageId) {
|
||||
const pageIdValidation = validateAlphanumericId(pageId, 'pageId', 255)
|
||||
if (!pageIdValidation.isValid) {
|
||||
return NextResponse.json({ error: pageIdValidation.error }, { status: 400 })
|
||||
}
|
||||
queryParams.append('page-id', pageId)
|
||||
}
|
||||
if (spaceId) {
|
||||
const spaceIdValidation = validateAlphanumericId(spaceId, 'spaceId', 255)
|
||||
if (!spaceIdValidation.isValid) {
|
||||
return NextResponse.json({ error: spaceIdValidation.error }, { status: 400 })
|
||||
}
|
||||
queryParams.append('space-id', spaceId)
|
||||
}
|
||||
if (assignedTo) {
|
||||
// Atlassian account IDs: 5d5bd05c3aee0123abc or 557058:6b9c9931-4693-49c1-8b3a-931f1af98134
|
||||
const assignedToValidation = validatePathSegment(assignedTo, {
|
||||
paramName: 'assignedTo',
|
||||
maxLength: 128,
|
||||
customPattern: /^[a-zA-Z0-9_|:-]+$/,
|
||||
})
|
||||
if (!assignedToValidation.isValid) {
|
||||
return NextResponse.json({ error: assignedToValidation.error }, { status: 400 })
|
||||
}
|
||||
queryParams.append('assigned-to', assignedTo)
|
||||
}
|
||||
|
||||
const url = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/api/v2/tasks?${queryParams.toString()}`
|
||||
|
||||
logger.info('Fetching tasks')
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
logger.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
})
|
||||
const errorMessage = errorData?.message || `Failed to list tasks (${response.status})`
|
||||
return NextResponse.json({ error: errorMessage }, { status: response.status })
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
const tasks = (data.results || []).map((task: any) => ({
|
||||
id: task.id,
|
||||
localId: task.localId ?? null,
|
||||
spaceId: task.spaceId ?? null,
|
||||
pageId: task.pageId ?? null,
|
||||
blogPostId: task.blogPostId ?? null,
|
||||
status: task.status,
|
||||
body: task.body?.storage?.value ?? null,
|
||||
createdBy: task.createdBy ?? null,
|
||||
assignedTo: task.assignedTo ?? null,
|
||||
completedBy: task.completedBy ?? null,
|
||||
createdAt: task.createdAt ?? null,
|
||||
updatedAt: task.updatedAt ?? null,
|
||||
dueAt: task.dueAt ?? null,
|
||||
completedAt: task.completedAt ?? null,
|
||||
}))
|
||||
|
||||
return NextResponse.json({
|
||||
tasks,
|
||||
nextCursor: data._links?.next
|
||||
? new URL(data._links.next, 'https://placeholder').searchParams.get('cursor')
|
||||
: null,
|
||||
})
|
||||
} catch (error) {
|
||||
logger.error('Error with tasks:', error)
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message || 'Internal server error' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
85
apps/sim/app/api/tools/confluence/user/route.ts
Normal file
85
apps/sim/app/api/tools/confluence/user/route.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { validateJiraCloudId, validatePathSegment } from '@/lib/core/security/input-validation'
|
||||
import { getConfluenceCloudId } from '@/tools/confluence/utils'
|
||||
|
||||
const logger = createLogger('ConfluenceUserAPI')
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
/**
|
||||
* Get a Confluence user by account ID.
|
||||
* Uses GET /wiki/rest/api/user?accountId={accountId}
|
||||
*/
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const auth = await checkSessionOrInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
const body = await request.json()
|
||||
const { domain, accessToken, accountId, cloudId: providedCloudId } = body
|
||||
|
||||
if (!domain) {
|
||||
return NextResponse.json({ error: 'Domain is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
if (!accessToken) {
|
||||
return NextResponse.json({ error: 'Access token is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
if (!accountId) {
|
||||
return NextResponse.json({ error: 'Account ID is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
// Atlassian account IDs: 5d5bd05c3aee0123abc or 557058:6b9c9931-4693-49c1-8b3a-931f1af98134
|
||||
const accountIdValidation = validatePathSegment(accountId, {
|
||||
paramName: 'accountId',
|
||||
maxLength: 128,
|
||||
customPattern: /^[a-zA-Z0-9_|:-]+$/,
|
||||
})
|
||||
if (!accountIdValidation.isValid) {
|
||||
return NextResponse.json({ error: accountIdValidation.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 })
|
||||
}
|
||||
|
||||
const url = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/rest/api/user?accountId=${encodeURIComponent(accountId)}`
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => null)
|
||||
logger.error('Confluence API error response:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: JSON.stringify(errorData, null, 2),
|
||||
})
|
||||
const errorMessage =
|
||||
errorData?.message || `Failed to get Confluence user (${response.status})`
|
||||
return NextResponse.json({ error: errorMessage }, { status: response.status })
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
return NextResponse.json(data)
|
||||
} catch (error) {
|
||||
logger.error('Error getting Confluence user:', error)
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message || 'Internal server error' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -987,7 +987,8 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
|
||||
const onChildWorkflowInstanceReady = (
|
||||
blockId: string,
|
||||
childWorkflowInstanceId: string,
|
||||
iterationContext?: IterationContext
|
||||
iterationContext?: IterationContext,
|
||||
executionOrder?: number
|
||||
) => {
|
||||
sendEvent({
|
||||
type: 'block:childWorkflowStarted',
|
||||
@@ -1001,6 +1002,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
|
||||
iterationCurrent: iterationContext.iterationCurrent,
|
||||
iterationContainerId: iterationContext.iterationContainerId,
|
||||
}),
|
||||
...(executionOrder !== undefined && { executionOrder }),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -40,10 +40,12 @@ const SCOPE_DESCRIPTIONS: Record<string, string> = {
|
||||
'https://www.googleapis.com/auth/drive.file': 'View and manage Google Drive files',
|
||||
'https://www.googleapis.com/auth/drive': 'Access all Google Drive files',
|
||||
'https://www.googleapis.com/auth/calendar': 'View and manage calendar',
|
||||
'https://www.googleapis.com/auth/tasks': 'Create, read, update, and delete Google Tasks',
|
||||
'https://www.googleapis.com/auth/userinfo.email': 'View email address',
|
||||
'https://www.googleapis.com/auth/userinfo.profile': 'View basic profile info',
|
||||
'https://www.googleapis.com/auth/forms.body': 'View and manage Google Forms',
|
||||
'https://www.googleapis.com/auth/forms.responses.readonly': 'View responses to Google Forms',
|
||||
'https://www.googleapis.com/auth/bigquery': 'View and manage data in Google BigQuery',
|
||||
'https://www.googleapis.com/auth/ediscovery': 'Access Google Vault for eDiscovery',
|
||||
'https://www.googleapis.com/auth/devstorage.read_only': 'Read files from Google Cloud Storage',
|
||||
'https://www.googleapis.com/auth/admin.directory.group': 'Manage Google Workspace groups',
|
||||
@@ -81,6 +83,15 @@ const SCOPE_DESCRIPTIONS: Record<string, string> = {
|
||||
'write:content.property:confluence': 'Create and manage content properties',
|
||||
'read:hierarchical-content:confluence': 'View page hierarchy (children and ancestors)',
|
||||
'read:content.metadata:confluence': 'View content metadata (required for ancestors)',
|
||||
'read:user:confluence': 'View Confluence user profiles',
|
||||
'read:task:confluence': 'View Confluence inline tasks',
|
||||
'write:task:confluence': 'Update Confluence inline tasks',
|
||||
'delete:blogpost:confluence': 'Delete Confluence blog posts',
|
||||
'write:space:confluence': 'Create and update Confluence spaces',
|
||||
'delete:space:confluence': 'Delete Confluence spaces',
|
||||
'read:space.property:confluence': 'View Confluence space properties',
|
||||
'write:space.property:confluence': 'Create and manage space properties',
|
||||
'read:space.permission:confluence': 'View Confluence space permissions',
|
||||
'read:me': 'Read profile information',
|
||||
'database.read': 'Read database',
|
||||
'database.write': 'Write to database',
|
||||
|
||||
@@ -379,7 +379,7 @@ export function CredentialSelector({
|
||||
filterOptions={true}
|
||||
isLoading={credentialsLoading}
|
||||
overlayContent={overlayContent}
|
||||
className={selectedId || isCredentialSetSelected ? 'pl-[28px]' : ''}
|
||||
className={overlayContent ? 'pl-[28px]' : ''}
|
||||
/>
|
||||
|
||||
{needsUpdate && (
|
||||
|
||||
@@ -41,6 +41,7 @@ import {
|
||||
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/hooks'
|
||||
import { Variables } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/variables/variables'
|
||||
import { useAutoLayout } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-auto-layout'
|
||||
import { useCurrentWorkflow } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-current-workflow'
|
||||
import { useWorkflowExecution } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution'
|
||||
import { getWorkflowLockToggleIds } from '@/app/workspace/[workspaceId]/w/[workflowId]/utils'
|
||||
import { useDeleteWorkflow, useImportWorkflow } from '@/app/workspace/[workspaceId]/w/hooks'
|
||||
@@ -203,6 +204,7 @@ export const Panel = memo(function Panel() {
|
||||
)
|
||||
|
||||
const currentWorkflow = activeWorkflowId ? workflows[activeWorkflowId] : null
|
||||
const { isSnapshotView } = useCurrentWorkflow()
|
||||
|
||||
/**
|
||||
* Mark hydration as complete on mount
|
||||
@@ -421,7 +423,7 @@ export const Panel = memo(function Panel() {
|
||||
<Layout className='h-3 w-3' animate={isAutoLayouting} variant='clockwise' />
|
||||
<span>Auto layout</span>
|
||||
</PopoverItem>
|
||||
{userPermissions.canAdmin && !currentWorkflow?.isSnapshotView && (
|
||||
{userPermissions.canAdmin && !isSnapshotView && (
|
||||
<PopoverItem onClick={handleToggleWorkflowLock} disabled={!hasBlocks}>
|
||||
{allBlocksLocked ? (
|
||||
<Unlock className='h-3 w-3' />
|
||||
|
||||
@@ -160,12 +160,16 @@ const IterationNodeRow = memo(function IterationNodeRow({
|
||||
onSelectEntry,
|
||||
isExpanded,
|
||||
onToggle,
|
||||
expandedNodes,
|
||||
onToggleNode,
|
||||
}: {
|
||||
node: EntryNode
|
||||
selectedEntryId: string | null
|
||||
onSelectEntry: (entry: ConsoleEntry) => void
|
||||
isExpanded: boolean
|
||||
onToggle: () => void
|
||||
expandedNodes: Set<string>
|
||||
onToggleNode: (nodeId: string) => void
|
||||
}) {
|
||||
const { entry, children, iterationInfo } = node
|
||||
const hasError = Boolean(entry.error) || children.some((c) => c.entry.error)
|
||||
@@ -226,11 +230,13 @@ const IterationNodeRow = memo(function IterationNodeRow({
|
||||
{isExpanded && hasChildren && (
|
||||
<div className={ROW_STYLES.nested}>
|
||||
{children.map((child) => (
|
||||
<BlockRow
|
||||
<EntryNodeRow
|
||||
key={child.entry.id}
|
||||
entry={child.entry}
|
||||
isSelected={selectedEntryId === child.entry.id}
|
||||
onSelect={onSelectEntry}
|
||||
node={child}
|
||||
selectedEntryId={selectedEntryId}
|
||||
onSelectEntry={onSelectEntry}
|
||||
expandedNodes={expandedNodes}
|
||||
onToggleNode={onToggleNode}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -346,6 +352,8 @@ const SubflowNodeRow = memo(function SubflowNodeRow({
|
||||
onSelectEntry={onSelectEntry}
|
||||
isExpanded={expandedNodes.has(iterNode.entry.id)}
|
||||
onToggle={() => onToggleNode(iterNode.entry.id)}
|
||||
expandedNodes={expandedNodes}
|
||||
onToggleNode={onToggleNode}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -520,6 +528,8 @@ const EntryNodeRow = memo(function EntryNodeRow({
|
||||
onSelectEntry={onSelectEntry}
|
||||
isExpanded={expandedNodes.has(node.entry.id)}
|
||||
onToggle={() => onToggleNode(node.entry.id)}
|
||||
expandedNodes={expandedNodes}
|
||||
onToggleNode={onToggleNode}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -554,6 +554,7 @@ export function useWorkflowExecution() {
|
||||
childWorkflowInstanceId: string
|
||||
iterationCurrent?: number
|
||||
iterationContainerId?: string
|
||||
executionOrder?: number
|
||||
}) => {
|
||||
if (isStaleExecution()) return
|
||||
updateConsole(
|
||||
@@ -564,6 +565,7 @@ export function useWorkflowExecution() {
|
||||
...(data.iterationContainerId !== undefined && {
|
||||
iterationContainerId: data.iterationContainerId,
|
||||
}),
|
||||
...(data.executionOrder !== undefined && { executionOrder: data.executionOrder }),
|
||||
},
|
||||
executionIdRef.current
|
||||
)
|
||||
|
||||
@@ -2534,6 +2534,16 @@ const WorkflowContent = React.memo(() => {
|
||||
window.removeEventListener('remove-from-subflow', handleRemoveFromSubflow as EventListener)
|
||||
}, [blocks, edgesForDisplay, getNodeAbsolutePosition, collaborativeBatchUpdateParent])
|
||||
|
||||
useEffect(() => {
|
||||
const handleToggleWorkflowLock = (e: CustomEvent<{ blockIds: string[] }>) => {
|
||||
collaborativeBatchToggleLocked(e.detail.blockIds)
|
||||
}
|
||||
|
||||
window.addEventListener('toggle-workflow-lock', handleToggleWorkflowLock as EventListener)
|
||||
return () =>
|
||||
window.removeEventListener('toggle-workflow-lock', handleToggleWorkflowLock as EventListener)
|
||||
}, [collaborativeBatchToggleLocked])
|
||||
|
||||
/**
|
||||
* Updates container dimensions in displayNodes during drag or keyboard movement.
|
||||
*/
|
||||
|
||||
@@ -281,6 +281,24 @@ interface ContextMenuProps {
|
||||
* Set to true when user cannot leave (e.g., last admin)
|
||||
*/
|
||||
disableLeave?: boolean
|
||||
/**
|
||||
* Callback when lock/unlock is clicked
|
||||
*/
|
||||
onToggleLock?: () => void
|
||||
/**
|
||||
* Whether to show the lock option (default: false)
|
||||
* Set to true for workflows that support locking
|
||||
*/
|
||||
showLock?: boolean
|
||||
/**
|
||||
* Whether the lock option is disabled (default: false)
|
||||
* Set to true when user lacks permissions
|
||||
*/
|
||||
disableLock?: boolean
|
||||
/**
|
||||
* Whether the workflow is currently locked (all blocks locked)
|
||||
*/
|
||||
isLocked?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -321,6 +339,10 @@ export function ContextMenu({
|
||||
onLeave,
|
||||
showLeave = false,
|
||||
disableLeave = false,
|
||||
onToggleLock,
|
||||
showLock = false,
|
||||
disableLock = false,
|
||||
isLocked = false,
|
||||
}: ContextMenuProps) {
|
||||
const [hexInput, setHexInput] = useState(currentColor || '#ffffff')
|
||||
|
||||
@@ -372,7 +394,8 @@ export function ContextMenu({
|
||||
(showRename && onRename) ||
|
||||
(showCreate && onCreate) ||
|
||||
(showCreateFolder && onCreateFolder) ||
|
||||
(showColorChange && onColorChange)
|
||||
(showColorChange && onColorChange) ||
|
||||
(showLock && onToggleLock)
|
||||
const hasCopySection = (showDuplicate && onDuplicate) || (showExport && onExport)
|
||||
|
||||
return (
|
||||
@@ -495,6 +518,19 @@ export function ContextMenu({
|
||||
</PopoverFolder>
|
||||
)}
|
||||
|
||||
{showLock && onToggleLock && (
|
||||
<PopoverItem
|
||||
rootOnly
|
||||
disabled={disableLock}
|
||||
onClick={() => {
|
||||
onToggleLock()
|
||||
onClose()
|
||||
}}
|
||||
>
|
||||
{isLocked ? 'Unlock' : 'Lock'}
|
||||
</PopoverItem>
|
||||
)}
|
||||
|
||||
{/* Copy and export actions */}
|
||||
{hasEditSection && hasCopySection && <PopoverDivider rootOnly />}
|
||||
{showDuplicate && onDuplicate && (
|
||||
|
||||
@@ -6,6 +6,7 @@ import { MoreHorizontal } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { useParams } from 'next/navigation'
|
||||
import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
|
||||
import { getWorkflowLockToggleIds } from '@/app/workspace/[workspaceId]/w/[workflowId]/utils'
|
||||
import { ContextMenu } from '@/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/context-menu/context-menu'
|
||||
import { DeleteModal } from '@/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/delete-modal/delete-modal'
|
||||
import { Avatars } from '@/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/workflow-item/avatars/avatars'
|
||||
@@ -27,6 +28,7 @@ import {
|
||||
import { useFolderStore } from '@/stores/folders/store'
|
||||
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
||||
import type { WorkflowMetadata } from '@/stores/workflows/registry/types'
|
||||
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
|
||||
|
||||
interface WorkflowItemProps {
|
||||
workflow: WorkflowMetadata
|
||||
@@ -169,6 +171,29 @@ export function WorkflowItem({
|
||||
[workflow.id, updateWorkflow]
|
||||
)
|
||||
|
||||
const activeWorkflowId = useWorkflowRegistry((state) => state.activeWorkflowId)
|
||||
const isActiveWorkflow = workflow.id === activeWorkflowId
|
||||
|
||||
const isWorkflowLocked = useWorkflowStore(
|
||||
useCallback(
|
||||
(state) => {
|
||||
if (!isActiveWorkflow) return false
|
||||
const blockValues = Object.values(state.blocks)
|
||||
if (blockValues.length === 0) return false
|
||||
return blockValues.every((block) => block.locked)
|
||||
},
|
||||
[isActiveWorkflow]
|
||||
)
|
||||
)
|
||||
|
||||
const handleToggleLock = useCallback(() => {
|
||||
if (!isActiveWorkflow) return
|
||||
const blocks = useWorkflowStore.getState().blocks
|
||||
const blockIds = getWorkflowLockToggleIds(blocks, !isWorkflowLocked)
|
||||
if (blockIds.length === 0) return
|
||||
window.dispatchEvent(new CustomEvent('toggle-workflow-lock', { detail: { blockIds } }))
|
||||
}, [isActiveWorkflow, isWorkflowLocked])
|
||||
|
||||
const isEditingRef = useRef(false)
|
||||
|
||||
const {
|
||||
@@ -461,6 +486,10 @@ export function WorkflowItem({
|
||||
disableExport={!userPermissions.canEdit}
|
||||
disableColorChange={!userPermissions.canEdit}
|
||||
disableDelete={!userPermissions.canEdit || !canDeleteSelection}
|
||||
onToggleLock={handleToggleLock}
|
||||
showLock={isActiveWorkflow && !isMixedSelection && selectedWorkflows.size <= 1}
|
||||
disableLock={!userPermissions.canAdmin}
|
||||
isLocked={isWorkflowLocked}
|
||||
/>
|
||||
|
||||
<DeleteModal
|
||||
|
||||
@@ -84,6 +84,7 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
|
||||
'write:content.property:confluence',
|
||||
'read:hierarchical-content:confluence',
|
||||
'read:content.metadata:confluence',
|
||||
'read:user:confluence',
|
||||
],
|
||||
placeholder: 'Select Confluence account',
|
||||
required: true,
|
||||
@@ -414,6 +415,8 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
|
||||
{ label: 'List Blog Posts', id: 'list_blogposts' },
|
||||
{ label: 'Get Blog Post', id: 'get_blogpost' },
|
||||
{ label: 'Create Blog Post', id: 'create_blogpost' },
|
||||
{ label: 'Update Blog Post', id: 'update_blogpost' },
|
||||
{ label: 'Delete Blog Post', id: 'delete_blogpost' },
|
||||
{ label: 'List Blog Posts in Space', id: 'list_blogposts_in_space' },
|
||||
// Comment Operations
|
||||
{ label: 'Create Comment', id: 'create_comment' },
|
||||
@@ -432,7 +435,24 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
|
||||
{ label: 'List Space Labels', id: 'list_space_labels' },
|
||||
// Space Operations
|
||||
{ label: 'Get Space', id: 'get_space' },
|
||||
{ label: 'Create Space', id: 'create_space' },
|
||||
{ label: 'Update Space', id: 'update_space' },
|
||||
{ label: 'Delete Space', id: 'delete_space' },
|
||||
{ label: 'List Spaces', id: 'list_spaces' },
|
||||
// Space Property Operations
|
||||
{ label: 'List Space Properties', id: 'list_space_properties' },
|
||||
{ label: 'Create Space Property', id: 'create_space_property' },
|
||||
{ label: 'Delete Space Property', id: 'delete_space_property' },
|
||||
// Space Permission Operations
|
||||
{ label: 'List Space Permissions', id: 'list_space_permissions' },
|
||||
// Page Descendant Operations
|
||||
{ label: 'Get Page Descendants', id: 'get_page_descendants' },
|
||||
// Task Operations
|
||||
{ label: 'List Tasks', id: 'list_tasks' },
|
||||
{ label: 'Get Task', id: 'get_task' },
|
||||
{ label: 'Update Task', id: 'update_task' },
|
||||
// User Operations
|
||||
{ label: 'Get User', id: 'get_user' },
|
||||
],
|
||||
value: () => 'read',
|
||||
},
|
||||
@@ -472,6 +492,15 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
|
||||
'write:content.property:confluence',
|
||||
'read:hierarchical-content:confluence',
|
||||
'read:content.metadata:confluence',
|
||||
'read:user:confluence',
|
||||
'read:task:confluence',
|
||||
'write:task:confluence',
|
||||
'delete:blogpost:confluence',
|
||||
'write:space:confluence',
|
||||
'delete:space:confluence',
|
||||
'read:space.property:confluence',
|
||||
'write:space.property:confluence',
|
||||
'read:space.permission:confluence',
|
||||
],
|
||||
placeholder: 'Select Confluence account',
|
||||
required: true,
|
||||
@@ -507,13 +536,26 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
|
||||
'list_pages_in_space',
|
||||
'list_blogposts',
|
||||
'get_blogpost',
|
||||
'update_blogpost',
|
||||
'delete_blogpost',
|
||||
'list_blogposts_in_space',
|
||||
'search',
|
||||
'search_in_space',
|
||||
'get_space',
|
||||
'create_space',
|
||||
'update_space',
|
||||
'delete_space',
|
||||
'list_spaces',
|
||||
'get_pages_by_label',
|
||||
'list_space_labels',
|
||||
'list_space_permissions',
|
||||
'list_space_properties',
|
||||
'create_space_property',
|
||||
'delete_space_property',
|
||||
'list_tasks',
|
||||
'get_task',
|
||||
'update_task',
|
||||
'get_user',
|
||||
],
|
||||
not: true,
|
||||
},
|
||||
@@ -537,6 +579,7 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
|
||||
'get_page_version',
|
||||
'list_page_properties',
|
||||
'create_page_property',
|
||||
'get_page_descendants',
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -553,13 +596,26 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
|
||||
'list_pages_in_space',
|
||||
'list_blogposts',
|
||||
'get_blogpost',
|
||||
'update_blogpost',
|
||||
'delete_blogpost',
|
||||
'list_blogposts_in_space',
|
||||
'search',
|
||||
'search_in_space',
|
||||
'get_space',
|
||||
'create_space',
|
||||
'update_space',
|
||||
'delete_space',
|
||||
'list_spaces',
|
||||
'get_pages_by_label',
|
||||
'list_space_labels',
|
||||
'list_space_permissions',
|
||||
'list_space_properties',
|
||||
'create_space_property',
|
||||
'delete_space_property',
|
||||
'list_tasks',
|
||||
'get_task',
|
||||
'update_task',
|
||||
'get_user',
|
||||
],
|
||||
not: true,
|
||||
},
|
||||
@@ -583,6 +639,7 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
|
||||
'get_page_version',
|
||||
'list_page_properties',
|
||||
'create_page_property',
|
||||
'get_page_descendants',
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -597,11 +654,17 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
|
||||
value: [
|
||||
'create',
|
||||
'get_space',
|
||||
'update_space',
|
||||
'delete_space',
|
||||
'list_pages_in_space',
|
||||
'search_in_space',
|
||||
'create_blogpost',
|
||||
'list_blogposts_in_space',
|
||||
'list_space_labels',
|
||||
'list_space_permissions',
|
||||
'list_space_properties',
|
||||
'create_space_property',
|
||||
'delete_space_property',
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -611,7 +674,10 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter blog post ID',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'get_blogpost' },
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['get_blogpost', 'update_blogpost', 'delete_blogpost'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'versionNumber',
|
||||
@@ -621,6 +687,86 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'get_page_version' },
|
||||
},
|
||||
{
|
||||
id: 'accountId',
|
||||
title: 'Account ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter Atlassian account ID',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'get_user' },
|
||||
},
|
||||
{
|
||||
id: 'taskId',
|
||||
title: 'Task ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter task ID',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: ['get_task', 'update_task'] },
|
||||
},
|
||||
{
|
||||
id: 'taskStatus',
|
||||
title: 'Task Status',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Complete', id: 'complete' },
|
||||
{ label: 'Incomplete', id: 'incomplete' },
|
||||
],
|
||||
value: () => 'complete',
|
||||
condition: { field: 'operation', value: 'update_task' },
|
||||
},
|
||||
{
|
||||
id: 'taskAssignedTo',
|
||||
title: 'Assigned To',
|
||||
type: 'short-input',
|
||||
placeholder: 'Filter by assignee account ID (optional)',
|
||||
condition: { field: 'operation', value: 'list_tasks' },
|
||||
},
|
||||
{
|
||||
id: 'spaceName',
|
||||
title: 'Space Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter space name',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'create_space' },
|
||||
},
|
||||
{
|
||||
id: 'spaceKey',
|
||||
title: 'Space Key',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter space key (e.g., MYSPACE)',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'create_space' },
|
||||
},
|
||||
{
|
||||
id: 'spaceDescription',
|
||||
title: 'Description',
|
||||
type: 'long-input',
|
||||
placeholder: 'Enter space description (optional)',
|
||||
condition: { field: 'operation', value: ['create_space', 'update_space'] },
|
||||
},
|
||||
{
|
||||
id: 'spacePropertyKey',
|
||||
title: 'Property Key',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter property key/name',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'create_space_property' },
|
||||
},
|
||||
{
|
||||
id: 'spacePropertyValue',
|
||||
title: 'Property Value',
|
||||
type: 'long-input',
|
||||
placeholder: 'Enter property value (JSON supported)',
|
||||
condition: { field: 'operation', value: 'create_space_property' },
|
||||
},
|
||||
{
|
||||
id: 'spacePropertyId',
|
||||
title: 'Property ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter property ID to delete',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'delete_space_property' },
|
||||
},
|
||||
{
|
||||
id: 'propertyKey',
|
||||
title: 'Property Key',
|
||||
@@ -650,14 +796,20 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
|
||||
title: 'Title',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter title',
|
||||
condition: { field: 'operation', value: ['create', 'update', 'create_blogpost'] },
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['create', 'update', 'create_blogpost', 'update_blogpost', 'update_space'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'content',
|
||||
title: 'Content',
|
||||
type: 'long-input',
|
||||
placeholder: 'Enter content',
|
||||
condition: { field: 'operation', value: ['create', 'update', 'create_blogpost'] },
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['create', 'update', 'create_blogpost', 'update_blogpost'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'parentId',
|
||||
@@ -813,6 +965,10 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
|
||||
'list_labels',
|
||||
'get_pages_by_label',
|
||||
'list_space_labels',
|
||||
'get_page_descendants',
|
||||
'list_space_permissions',
|
||||
'list_space_properties',
|
||||
'list_tasks',
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -836,6 +992,10 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
|
||||
'list_labels',
|
||||
'get_pages_by_label',
|
||||
'list_space_labels',
|
||||
'get_page_descendants',
|
||||
'list_space_permissions',
|
||||
'list_space_properties',
|
||||
'list_tasks',
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -921,7 +1081,27 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
|
||||
'confluence_list_space_labels',
|
||||
// Space Tools
|
||||
'confluence_get_space',
|
||||
'confluence_create_space',
|
||||
'confluence_update_space',
|
||||
'confluence_delete_space',
|
||||
'confluence_list_spaces',
|
||||
// Space Property Tools
|
||||
'confluence_list_space_properties',
|
||||
'confluence_create_space_property',
|
||||
'confluence_delete_space_property',
|
||||
// Space Permission Tools
|
||||
'confluence_list_space_permissions',
|
||||
// Page Descendant Tools
|
||||
'confluence_get_page_descendants',
|
||||
// Task Tools
|
||||
'confluence_list_tasks',
|
||||
'confluence_get_task',
|
||||
'confluence_update_task',
|
||||
// Blog Post Update/Delete
|
||||
'confluence_update_blogpost',
|
||||
'confluence_delete_blogpost',
|
||||
// User Tools
|
||||
'confluence_get_user',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => {
|
||||
@@ -965,6 +1145,10 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
|
||||
return 'confluence_get_blogpost'
|
||||
case 'create_blogpost':
|
||||
return 'confluence_create_blogpost'
|
||||
case 'update_blogpost':
|
||||
return 'confluence_update_blogpost'
|
||||
case 'delete_blogpost':
|
||||
return 'confluence_delete_blogpost'
|
||||
case 'list_blogposts_in_space':
|
||||
return 'confluence_list_blogposts_in_space'
|
||||
// Comment Operations
|
||||
@@ -997,8 +1181,37 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
|
||||
// Space Operations
|
||||
case 'get_space':
|
||||
return 'confluence_get_space'
|
||||
case 'create_space':
|
||||
return 'confluence_create_space'
|
||||
case 'update_space':
|
||||
return 'confluence_update_space'
|
||||
case 'delete_space':
|
||||
return 'confluence_delete_space'
|
||||
case 'list_spaces':
|
||||
return 'confluence_list_spaces'
|
||||
// Space Property Operations
|
||||
case 'list_space_properties':
|
||||
return 'confluence_list_space_properties'
|
||||
case 'create_space_property':
|
||||
return 'confluence_create_space_property'
|
||||
case 'delete_space_property':
|
||||
return 'confluence_delete_space_property'
|
||||
// Space Permission Operations
|
||||
case 'list_space_permissions':
|
||||
return 'confluence_list_space_permissions'
|
||||
// Page Descendant Operations
|
||||
case 'get_page_descendants':
|
||||
return 'confluence_get_page_descendants'
|
||||
// Task Operations
|
||||
case 'list_tasks':
|
||||
return 'confluence_list_tasks'
|
||||
case 'get_task':
|
||||
return 'confluence_get_task'
|
||||
case 'update_task':
|
||||
return 'confluence_update_task'
|
||||
// User Operations
|
||||
case 'get_user':
|
||||
return 'confluence_get_user'
|
||||
default:
|
||||
return 'confluence_retrieve'
|
||||
}
|
||||
@@ -1013,6 +1226,7 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
|
||||
attachmentComment,
|
||||
blogPostId,
|
||||
versionNumber,
|
||||
accountId,
|
||||
propertyKey,
|
||||
propertyValue,
|
||||
propertyId,
|
||||
@@ -1022,6 +1236,15 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
|
||||
purge,
|
||||
bodyFormat,
|
||||
cursor,
|
||||
taskId,
|
||||
taskStatus,
|
||||
taskAssignedTo,
|
||||
spaceName,
|
||||
spaceKey,
|
||||
spaceDescription,
|
||||
spacePropertyKey,
|
||||
spacePropertyValue,
|
||||
spacePropertyId,
|
||||
...rest
|
||||
} = params
|
||||
|
||||
@@ -1069,8 +1292,8 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
|
||||
}
|
||||
|
||||
// Operations that support generic cursor pagination.
|
||||
// get_pages_by_label and list_space_labels have dedicated handlers
|
||||
// below that pass cursor along with their required params (labelId, spaceId).
|
||||
// get_pages_by_label, list_space_labels, and list_tasks have dedicated handlers
|
||||
// below that pass cursor along with their required params.
|
||||
const supportsCursor = [
|
||||
'list_attachments',
|
||||
'list_spaces',
|
||||
@@ -1081,6 +1304,9 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
|
||||
'list_page_versions',
|
||||
'list_page_properties',
|
||||
'list_labels',
|
||||
'get_page_descendants',
|
||||
'list_space_permissions',
|
||||
'list_space_properties',
|
||||
]
|
||||
|
||||
if (supportsCursor.includes(operation) && cursor) {
|
||||
@@ -1152,6 +1378,122 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
|
||||
}
|
||||
}
|
||||
|
||||
if (operation === 'get_user') {
|
||||
return {
|
||||
credential: oauthCredential,
|
||||
operation,
|
||||
accountId: accountId ? String(accountId).trim() : undefined,
|
||||
...rest,
|
||||
}
|
||||
}
|
||||
|
||||
if (operation === 'update_blogpost' || operation === 'delete_blogpost') {
|
||||
return {
|
||||
credential: oauthCredential,
|
||||
operation,
|
||||
blogPostId: blogPostId || undefined,
|
||||
...rest,
|
||||
}
|
||||
}
|
||||
|
||||
if (operation === 'create_space') {
|
||||
return {
|
||||
credential: oauthCredential,
|
||||
operation,
|
||||
name: spaceName,
|
||||
key: spaceKey,
|
||||
description: spaceDescription,
|
||||
...rest,
|
||||
}
|
||||
}
|
||||
|
||||
if (operation === 'update_space') {
|
||||
return {
|
||||
credential: oauthCredential,
|
||||
operation,
|
||||
name: spaceName || rest.title,
|
||||
description: spaceDescription,
|
||||
...rest,
|
||||
}
|
||||
}
|
||||
|
||||
if (operation === 'delete_space') {
|
||||
return {
|
||||
credential: oauthCredential,
|
||||
operation,
|
||||
...rest,
|
||||
}
|
||||
}
|
||||
|
||||
if (operation === 'create_space_property') {
|
||||
return {
|
||||
credential: oauthCredential,
|
||||
operation,
|
||||
key: spacePropertyKey,
|
||||
value: spacePropertyValue,
|
||||
...rest,
|
||||
}
|
||||
}
|
||||
|
||||
if (operation === 'delete_space_property') {
|
||||
return {
|
||||
credential: oauthCredential,
|
||||
operation,
|
||||
propertyId: spacePropertyId,
|
||||
...rest,
|
||||
}
|
||||
}
|
||||
|
||||
if (operation === 'list_space_permissions' || operation === 'list_space_properties') {
|
||||
return {
|
||||
credential: oauthCredential,
|
||||
operation,
|
||||
cursor: cursor || undefined,
|
||||
...rest,
|
||||
}
|
||||
}
|
||||
|
||||
if (operation === 'get_page_descendants') {
|
||||
return {
|
||||
credential: oauthCredential,
|
||||
pageId: effectivePageId,
|
||||
operation,
|
||||
cursor: cursor || undefined,
|
||||
...rest,
|
||||
}
|
||||
}
|
||||
|
||||
if (operation === 'get_task') {
|
||||
return {
|
||||
credential: oauthCredential,
|
||||
operation,
|
||||
taskId,
|
||||
...rest,
|
||||
}
|
||||
}
|
||||
|
||||
if (operation === 'update_task') {
|
||||
return {
|
||||
credential: oauthCredential,
|
||||
operation,
|
||||
taskId,
|
||||
status: taskStatus,
|
||||
...rest,
|
||||
}
|
||||
}
|
||||
|
||||
if (operation === 'list_tasks') {
|
||||
return {
|
||||
credential: oauthCredential,
|
||||
operation,
|
||||
pageId: effectivePageId || undefined,
|
||||
assignedTo: taskAssignedTo || undefined,
|
||||
status: taskStatus || undefined,
|
||||
cursor: cursor || undefined,
|
||||
...rest,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
credential: oauthCredential,
|
||||
pageId: effectivePageId || undefined,
|
||||
@@ -1171,6 +1513,7 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
|
||||
spaceId: { type: 'string', description: 'Space identifier' },
|
||||
blogPostId: { type: 'string', description: 'Blog post identifier' },
|
||||
versionNumber: { type: 'number', description: 'Page version number' },
|
||||
accountId: { type: 'string', description: 'Atlassian account ID' },
|
||||
propertyKey: { type: 'string', description: 'Property key/name' },
|
||||
propertyValue: { type: 'json', description: 'Property value (JSON)' },
|
||||
title: { type: 'string', description: 'Page or blog post title' },
|
||||
@@ -1192,6 +1535,15 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
|
||||
bodyFormat: { type: 'string', description: 'Body format for comments' },
|
||||
limit: { type: 'number', description: 'Maximum number of results' },
|
||||
cursor: { type: 'string', description: 'Pagination cursor from previous response' },
|
||||
taskId: { type: 'string', description: 'Task identifier' },
|
||||
taskStatus: { type: 'string', description: 'Task status (complete or incomplete)' },
|
||||
taskAssignedTo: { type: 'string', description: 'Filter tasks by assignee account ID' },
|
||||
spaceName: { type: 'string', description: 'Space name for create/update' },
|
||||
spaceKey: { type: 'string', description: 'Space key for create' },
|
||||
spaceDescription: { type: 'string', description: 'Space description' },
|
||||
spacePropertyKey: { type: 'string', description: 'Space property key' },
|
||||
spacePropertyValue: { type: 'json', description: 'Space property value' },
|
||||
spacePropertyId: { type: 'string', description: 'Space property identifier' },
|
||||
},
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp' },
|
||||
@@ -1242,6 +1594,23 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
|
||||
propertyId: { type: 'string', description: 'Property identifier' },
|
||||
propertyKey: { type: 'string', description: 'Property key' },
|
||||
propertyValue: { type: 'json', description: 'Property value' },
|
||||
// User Results
|
||||
accountId: { type: 'string', description: 'Atlassian account ID' },
|
||||
displayName: { type: 'string', description: 'User display name' },
|
||||
email: { type: 'string', description: 'User email address' },
|
||||
accountType: { type: 'string', description: 'Account type (atlassian, app, customer)' },
|
||||
profilePicture: { type: 'string', description: 'Path to user profile picture' },
|
||||
publicName: { type: 'string', description: 'User public name' },
|
||||
// Task Results
|
||||
tasks: { type: 'array', description: 'List of tasks' },
|
||||
taskId: { type: 'string', description: 'Task identifier' },
|
||||
// Descendant Results
|
||||
descendants: { type: 'array', description: 'List of descendant pages' },
|
||||
// Permission Results
|
||||
permissions: { type: 'array', description: 'List of space permissions' },
|
||||
// Space Property Results
|
||||
homepageId: { type: 'string', description: 'Space homepage ID' },
|
||||
description: { type: 'json', description: 'Space description' },
|
||||
// Pagination
|
||||
nextCursor: { type: 'string', description: 'Cursor for fetching next page of results' },
|
||||
},
|
||||
|
||||
187
apps/sim/blocks/blocks/devin.ts
Normal file
187
apps/sim/blocks/blocks/devin.ts
Normal file
@@ -0,0 +1,187 @@
|
||||
import { DevinIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
|
||||
export const DevinBlock: BlockConfig = {
|
||||
type: 'devin',
|
||||
name: 'Devin',
|
||||
description: 'Autonomous AI software engineer',
|
||||
longDescription:
|
||||
'Integrate Devin into your workflow. Create sessions to assign coding tasks, send messages to guide active sessions, and retrieve session status and results. Devin autonomously writes, runs, and tests code.',
|
||||
bestPractices: `
|
||||
- Write clear, specific prompts describing the task, expected outcome, and any constraints.
|
||||
- Use playbook IDs to standardize recurring task patterns across sessions.
|
||||
- Set ACU limits to control cost for long-running tasks.
|
||||
- Use Get Session to poll for completion status before consuming structured output.
|
||||
- Send Message auto-resumes suspended sessions — no need to resume separately.
|
||||
`,
|
||||
docsLink: 'https://docs.sim.ai/tools/devin',
|
||||
category: 'tools',
|
||||
bgColor: '#12141A',
|
||||
icon: DevinIcon,
|
||||
authMode: AuthMode.ApiKey,
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Create Session', id: 'create_session' },
|
||||
{ label: 'Get Session', id: 'get_session' },
|
||||
{ label: 'List Sessions', id: 'list_sessions' },
|
||||
{ label: 'Send Message', id: 'send_message' },
|
||||
],
|
||||
value: () => 'create_session',
|
||||
},
|
||||
{
|
||||
id: 'apiKey',
|
||||
title: 'API Key',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter your Devin API key (cog_...)',
|
||||
password: true,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'prompt',
|
||||
title: 'Prompt',
|
||||
type: 'long-input',
|
||||
placeholder: 'Describe the task for Devin...',
|
||||
required: { field: 'operation', value: 'create_session' },
|
||||
condition: { field: 'operation', value: 'create_session' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `You are an expert at writing clear, actionable prompts for Devin, an autonomous AI software engineer. Generate or refine a task prompt based on the user's request.
|
||||
|
||||
Current prompt: {context}
|
||||
|
||||
RULES:
|
||||
1. Be specific about the expected outcome and deliverables
|
||||
2. Include relevant technical context (languages, frameworks, repos)
|
||||
3. Specify any constraints (don't modify certain files, follow certain patterns)
|
||||
4. Break complex tasks into clear steps when helpful
|
||||
5. Return ONLY the prompt text, no markdown formatting or explanations`,
|
||||
placeholder: 'Describe what you want Devin to do...',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'playbookId',
|
||||
title: 'Playbook ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Optional playbook ID to guide the session',
|
||||
condition: { field: 'operation', value: 'create_session' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'maxAcuLimit',
|
||||
title: 'Max ACU Limit',
|
||||
type: 'short-input',
|
||||
placeholder: 'Maximum ACU budget for this session',
|
||||
condition: { field: 'operation', value: 'create_session' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'tags',
|
||||
title: 'Tags',
|
||||
type: 'short-input',
|
||||
placeholder: 'Comma-separated tags',
|
||||
condition: { field: 'operation', value: 'create_session' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'sessionId',
|
||||
title: 'Session ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter session ID',
|
||||
required: { field: 'operation', value: ['get_session', 'send_message'] },
|
||||
condition: { field: 'operation', value: ['get_session', 'send_message'] },
|
||||
},
|
||||
{
|
||||
id: 'message',
|
||||
title: 'Message',
|
||||
type: 'long-input',
|
||||
placeholder: 'Enter message to send to Devin...',
|
||||
required: { field: 'operation', value: 'send_message' },
|
||||
condition: { field: 'operation', value: 'send_message' },
|
||||
},
|
||||
{
|
||||
id: 'limit',
|
||||
title: 'Limit',
|
||||
type: 'short-input',
|
||||
placeholder: 'Number of sessions (1-200, default: 100)',
|
||||
condition: { field: 'operation', value: 'list_sessions' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: [
|
||||
'devin_create_session',
|
||||
'devin_get_session',
|
||||
'devin_list_sessions',
|
||||
'devin_send_message',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => `devin_${params.operation}`,
|
||||
params: (params) => {
|
||||
if (params.maxAcuLimit != null && params.maxAcuLimit !== '') {
|
||||
params.maxAcuLimit = Number(params.maxAcuLimit)
|
||||
}
|
||||
if (params.limit != null && params.limit !== '') {
|
||||
params.limit = Number(params.limit)
|
||||
}
|
||||
return params
|
||||
},
|
||||
},
|
||||
},
|
||||
inputs: {
|
||||
prompt: { type: 'string', description: 'Task prompt for Devin' },
|
||||
sessionId: { type: 'string', description: 'Session ID' },
|
||||
message: { type: 'string', description: 'Message to send to the session' },
|
||||
apiKey: { type: 'string', description: 'Devin API key' },
|
||||
playbookId: { type: 'string', description: 'Playbook ID to guide the session' },
|
||||
maxAcuLimit: { type: 'number', description: 'Maximum ACU limit' },
|
||||
tags: { type: 'string', description: 'Comma-separated tags' },
|
||||
limit: { type: 'number', description: 'Number of sessions to return' },
|
||||
},
|
||||
outputs: {
|
||||
sessionId: { type: 'string', description: 'Session identifier' },
|
||||
url: { type: 'string', description: 'URL to view the session in Devin UI' },
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Session status (new, claimed, running, exit, error, suspended, resuming)',
|
||||
},
|
||||
statusDetail: {
|
||||
type: 'string',
|
||||
description: 'Detailed status (working, waiting_for_user, finished, etc.)',
|
||||
condition: { field: 'operation', value: 'list_sessions', not: true },
|
||||
},
|
||||
title: { type: 'string', description: 'Session title' },
|
||||
createdAt: { type: 'number', description: 'Creation timestamp (Unix)' },
|
||||
updatedAt: { type: 'number', description: 'Last updated timestamp (Unix)' },
|
||||
acusConsumed: {
|
||||
type: 'number',
|
||||
description: 'ACUs consumed',
|
||||
condition: { field: 'operation', value: 'list_sessions', not: true },
|
||||
},
|
||||
tags: { type: 'json', description: 'Session tags' },
|
||||
pullRequests: {
|
||||
type: 'json',
|
||||
description: 'Pull requests created during the session',
|
||||
condition: { field: 'operation', value: 'list_sessions', not: true },
|
||||
},
|
||||
structuredOutput: {
|
||||
type: 'json',
|
||||
description: 'Structured output from the session',
|
||||
condition: { field: 'operation', value: 'list_sessions', not: true },
|
||||
},
|
||||
playbookId: {
|
||||
type: 'string',
|
||||
description: 'Associated playbook ID',
|
||||
condition: { field: 'operation', value: 'list_sessions', not: true },
|
||||
},
|
||||
sessions: {
|
||||
type: 'json',
|
||||
description: 'List of sessions',
|
||||
condition: { field: 'operation', value: 'list_sessions' },
|
||||
},
|
||||
},
|
||||
}
|
||||
256
apps/sim/blocks/blocks/google_bigquery.ts
Normal file
256
apps/sim/blocks/blocks/google_bigquery.ts
Normal file
@@ -0,0 +1,256 @@
|
||||
import { GoogleBigQueryIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
|
||||
export const GoogleBigQueryBlock: BlockConfig = {
|
||||
type: 'google_bigquery',
|
||||
name: 'Google BigQuery',
|
||||
description: 'Query, list, and insert data in Google BigQuery',
|
||||
longDescription:
|
||||
'Connect to Google BigQuery to run SQL queries, list datasets and tables, get table metadata, and insert rows.',
|
||||
docsLink: 'https://docs.sim.ai/tools/google_bigquery',
|
||||
category: 'tools',
|
||||
bgColor: '#E0E0E0',
|
||||
icon: GoogleBigQueryIcon,
|
||||
authMode: AuthMode.OAuth,
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Run Query', id: 'query' },
|
||||
{ label: 'List Datasets', id: 'list_datasets' },
|
||||
{ label: 'List Tables', id: 'list_tables' },
|
||||
{ label: 'Get Table', id: 'get_table' },
|
||||
{ label: 'Insert Rows', id: 'insert_rows' },
|
||||
],
|
||||
value: () => 'query',
|
||||
},
|
||||
|
||||
{
|
||||
id: 'credential',
|
||||
title: 'Google Account',
|
||||
type: 'oauth-input',
|
||||
canonicalParamId: 'oauthCredential',
|
||||
mode: 'basic',
|
||||
required: true,
|
||||
serviceId: 'google-bigquery',
|
||||
requiredScopes: ['https://www.googleapis.com/auth/bigquery'],
|
||||
placeholder: 'Select Google account',
|
||||
},
|
||||
{
|
||||
id: 'manualCredential',
|
||||
title: 'Google Account',
|
||||
type: 'short-input',
|
||||
canonicalParamId: 'oauthCredential',
|
||||
mode: 'advanced',
|
||||
placeholder: 'Enter credential ID',
|
||||
required: true,
|
||||
},
|
||||
|
||||
{
|
||||
id: 'projectId',
|
||||
title: 'Project ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter Google Cloud project ID',
|
||||
required: true,
|
||||
},
|
||||
|
||||
{
|
||||
id: 'query',
|
||||
title: 'SQL Query',
|
||||
type: 'long-input',
|
||||
placeholder: 'SELECT * FROM `project.dataset.table` LIMIT 100',
|
||||
condition: { field: 'operation', value: 'query' },
|
||||
required: { field: 'operation', value: 'query' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a BigQuery Standard SQL query based on the user's description.
|
||||
The query should:
|
||||
- Use Standard SQL syntax (not Legacy SQL)
|
||||
- Be well-formatted and efficient
|
||||
- Include appropriate LIMIT clauses when applicable
|
||||
|
||||
Examples:
|
||||
- "get all users" -> SELECT * FROM \`project.dataset.users\` LIMIT 1000
|
||||
- "count orders by status" -> SELECT status, COUNT(*) as count FROM \`project.dataset.orders\` GROUP BY status
|
||||
- "recent events" -> SELECT * FROM \`project.dataset.events\` ORDER BY created_at DESC LIMIT 100
|
||||
|
||||
Return ONLY the SQL query - no explanations, no quotes, no extra text.`,
|
||||
placeholder: 'Describe the query you want to run...',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'useLegacySql',
|
||||
title: 'Use Legacy SQL',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'query' },
|
||||
},
|
||||
{
|
||||
id: 'maxResults',
|
||||
title: 'Max Results',
|
||||
type: 'short-input',
|
||||
placeholder: 'Maximum rows to return',
|
||||
condition: { field: 'operation', value: ['query', 'list_datasets', 'list_tables'] },
|
||||
},
|
||||
{
|
||||
id: 'defaultDatasetId',
|
||||
title: 'Default Dataset',
|
||||
type: 'short-input',
|
||||
placeholder: 'Default dataset for unqualified table names',
|
||||
condition: { field: 'operation', value: 'query' },
|
||||
},
|
||||
{
|
||||
id: 'location',
|
||||
title: 'Location',
|
||||
type: 'short-input',
|
||||
placeholder: 'Processing location (e.g., US, EU)',
|
||||
condition: { field: 'operation', value: 'query' },
|
||||
},
|
||||
|
||||
{
|
||||
id: 'datasetId',
|
||||
title: 'Dataset ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter BigQuery dataset ID',
|
||||
condition: { field: 'operation', value: ['list_tables', 'get_table', 'insert_rows'] },
|
||||
required: { field: 'operation', value: ['list_tables', 'get_table', 'insert_rows'] },
|
||||
},
|
||||
|
||||
{
|
||||
id: 'tableId',
|
||||
title: 'Table ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter BigQuery table ID',
|
||||
condition: { field: 'operation', value: ['get_table', 'insert_rows'] },
|
||||
required: { field: 'operation', value: ['get_table', 'insert_rows'] },
|
||||
},
|
||||
|
||||
{
|
||||
id: 'rows',
|
||||
title: 'Rows',
|
||||
type: 'long-input',
|
||||
placeholder: '[{"column1": "value1", "column2": 42}]',
|
||||
condition: { field: 'operation', value: 'insert_rows' },
|
||||
required: { field: 'operation', value: 'insert_rows' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a JSON array of row objects for BigQuery insertion based on the user's description.
|
||||
Each row should be a JSON object where keys are column names and values match the expected types.
|
||||
|
||||
Examples:
|
||||
- "3 users" -> [{"name": "Alice", "email": "alice@example.com"}, {"name": "Bob", "email": "bob@example.com"}, {"name": "Charlie", "email": "charlie@example.com"}]
|
||||
- "order record" -> [{"order_id": "ORD-001", "amount": 99.99, "status": "pending"}]
|
||||
|
||||
Return ONLY the JSON array - no explanations, no wrapping, no extra text.`,
|
||||
placeholder: 'Describe the rows to insert...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'skipInvalidRows',
|
||||
title: 'Skip Invalid Rows',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'insert_rows' },
|
||||
},
|
||||
{
|
||||
id: 'ignoreUnknownValues',
|
||||
title: 'Ignore Unknown Values',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'insert_rows' },
|
||||
},
|
||||
|
||||
{
|
||||
id: 'pageToken',
|
||||
title: 'Page Token',
|
||||
type: 'short-input',
|
||||
placeholder: 'Pagination token',
|
||||
condition: { field: 'operation', value: ['list_datasets', 'list_tables'] },
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: [
|
||||
'google_bigquery_query',
|
||||
'google_bigquery_list_datasets',
|
||||
'google_bigquery_list_tables',
|
||||
'google_bigquery_get_table',
|
||||
'google_bigquery_insert_rows',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => {
|
||||
switch (params.operation) {
|
||||
case 'query':
|
||||
return 'google_bigquery_query'
|
||||
case 'list_datasets':
|
||||
return 'google_bigquery_list_datasets'
|
||||
case 'list_tables':
|
||||
return 'google_bigquery_list_tables'
|
||||
case 'get_table':
|
||||
return 'google_bigquery_get_table'
|
||||
case 'insert_rows':
|
||||
return 'google_bigquery_insert_rows'
|
||||
default:
|
||||
throw new Error(`Invalid Google BigQuery operation: ${params.operation}`)
|
||||
}
|
||||
},
|
||||
params: (params) => {
|
||||
const { oauthCredential, rows, maxResults, ...rest } = params
|
||||
return {
|
||||
...rest,
|
||||
oauthCredential,
|
||||
...(rows && { rows: typeof rows === 'string' ? rows : JSON.stringify(rows) }),
|
||||
...(maxResults !== undefined && maxResults !== '' && { maxResults: Number(maxResults) }),
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'Operation to perform' },
|
||||
oauthCredential: { type: 'string', description: 'Google BigQuery OAuth credential' },
|
||||
projectId: { type: 'string', description: 'Google Cloud project ID' },
|
||||
query: { type: 'string', description: 'SQL query to execute' },
|
||||
useLegacySql: { type: 'boolean', description: 'Whether to use legacy SQL syntax' },
|
||||
maxResults: { type: 'number', description: 'Maximum number of results to return' },
|
||||
defaultDatasetId: {
|
||||
type: 'string',
|
||||
description: 'Default dataset for unqualified table names',
|
||||
},
|
||||
location: { type: 'string', description: 'Processing location' },
|
||||
datasetId: { type: 'string', description: 'BigQuery dataset ID' },
|
||||
tableId: { type: 'string', description: 'BigQuery table ID' },
|
||||
rows: { type: 'string', description: 'JSON array of row objects to insert' },
|
||||
skipInvalidRows: { type: 'boolean', description: 'Whether to skip invalid rows during insert' },
|
||||
ignoreUnknownValues: {
|
||||
type: 'boolean',
|
||||
description: 'Whether to ignore unknown column values',
|
||||
},
|
||||
pageToken: { type: 'string', description: 'Pagination token' },
|
||||
},
|
||||
outputs: {
|
||||
columns: { type: 'json', description: 'Array of column names (query)' },
|
||||
rows: { type: 'json', description: 'Array of row objects (query)' },
|
||||
totalRows: { type: 'string', description: 'Total number of rows (query)' },
|
||||
jobComplete: { type: 'boolean', description: 'Whether the query completed (query)' },
|
||||
totalBytesProcessed: { type: 'string', description: 'Bytes processed (query)' },
|
||||
cacheHit: { type: 'boolean', description: 'Whether result was cached (query)' },
|
||||
jobReference: { type: 'json', description: 'Job reference for incomplete queries (query)' },
|
||||
pageToken: { type: 'string', description: 'Token for additional result pages (query)' },
|
||||
datasets: { type: 'json', description: 'Array of dataset objects (list_datasets)' },
|
||||
tables: { type: 'json', description: 'Array of table objects (list_tables)' },
|
||||
totalItems: { type: 'number', description: 'Total items count (list_tables)' },
|
||||
tableId: { type: 'string', description: 'Table ID (get_table)' },
|
||||
datasetId: { type: 'string', description: 'Dataset ID (get_table)' },
|
||||
type: { type: 'string', description: 'Table type (get_table)' },
|
||||
description: { type: 'string', description: 'Table description (get_table)' },
|
||||
numRows: { type: 'string', description: 'Row count (get_table)' },
|
||||
numBytes: { type: 'string', description: 'Size in bytes (get_table)' },
|
||||
schema: { type: 'json', description: 'Column definitions (get_table)' },
|
||||
creationTime: { type: 'string', description: 'Creation time (get_table)' },
|
||||
lastModifiedTime: { type: 'string', description: 'Last modified time (get_table)' },
|
||||
location: { type: 'string', description: 'Data location (get_table)' },
|
||||
insertedRows: { type: 'number', description: 'Rows inserted (insert_rows)' },
|
||||
errors: { type: 'json', description: 'Insert errors (insert_rows)' },
|
||||
nextPageToken: { type: 'string', description: 'Token for next page of results' },
|
||||
},
|
||||
}
|
||||
262
apps/sim/blocks/blocks/google_tasks.ts
Normal file
262
apps/sim/blocks/blocks/google_tasks.ts
Normal file
@@ -0,0 +1,262 @@
|
||||
import { GoogleTasksIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import type { GoogleTasksResponse } from '@/tools/google_tasks/types'
|
||||
|
||||
export const GoogleTasksBlock: BlockConfig<GoogleTasksResponse> = {
|
||||
type: 'google_tasks',
|
||||
name: 'Google Tasks',
|
||||
description: 'Manage Google Tasks',
|
||||
longDescription:
|
||||
'Integrate Google Tasks into your workflow. Create, read, update, delete, and list tasks and task lists.',
|
||||
docsLink: 'https://docs.sim.ai/tools/google_tasks',
|
||||
category: 'tools',
|
||||
bgColor: '#E0E0E0',
|
||||
icon: GoogleTasksIcon,
|
||||
authMode: AuthMode.OAuth,
|
||||
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Create Task', id: 'create' },
|
||||
{ label: 'List Tasks', id: 'list' },
|
||||
{ label: 'Get Task', id: 'get' },
|
||||
{ label: 'Update Task', id: 'update' },
|
||||
{ label: 'Delete Task', id: 'delete' },
|
||||
{ label: 'List Task Lists', id: 'list_task_lists' },
|
||||
],
|
||||
value: () => 'create',
|
||||
},
|
||||
{
|
||||
id: 'credential',
|
||||
title: 'Google Tasks Account',
|
||||
type: 'oauth-input',
|
||||
canonicalParamId: 'oauthCredential',
|
||||
mode: 'basic',
|
||||
required: true,
|
||||
serviceId: 'google-tasks',
|
||||
requiredScopes: ['https://www.googleapis.com/auth/tasks'],
|
||||
placeholder: 'Select Google Tasks account',
|
||||
},
|
||||
{
|
||||
id: 'manualCredential',
|
||||
title: 'Google Tasks Account',
|
||||
type: 'short-input',
|
||||
canonicalParamId: 'oauthCredential',
|
||||
mode: 'advanced',
|
||||
placeholder: 'Enter credential ID',
|
||||
required: true,
|
||||
},
|
||||
|
||||
// Task List ID - shown for all task operations (not list_task_lists)
|
||||
{
|
||||
id: 'taskListId',
|
||||
title: 'Task List ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Task list ID (leave empty for default list)',
|
||||
condition: { field: 'operation', value: 'list_task_lists', not: true },
|
||||
},
|
||||
|
||||
// Create Task Fields
|
||||
{
|
||||
id: 'title',
|
||||
title: 'Title',
|
||||
type: 'short-input',
|
||||
placeholder: 'Buy groceries',
|
||||
condition: { field: 'operation', value: 'create' },
|
||||
required: { field: 'operation', value: 'create' },
|
||||
},
|
||||
{
|
||||
id: 'notes',
|
||||
title: 'Notes',
|
||||
type: 'long-input',
|
||||
placeholder: 'Task notes or description',
|
||||
condition: { field: 'operation', value: 'create' },
|
||||
},
|
||||
{
|
||||
id: 'due',
|
||||
title: 'Due Date',
|
||||
type: 'short-input',
|
||||
placeholder: '2025-06-03T00:00:00.000Z',
|
||||
condition: { field: 'operation', value: 'create' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate an RFC 3339 timestamp in UTC based on the user's description.
|
||||
The timestamp should be in the format: YYYY-MM-DDTHH:MM:SS.000Z (UTC timezone).
|
||||
Examples:
|
||||
- "tomorrow" -> Calculate tomorrow's date at 00:00:00.000Z
|
||||
- "next Friday" -> Calculate the next Friday's date at 00:00:00.000Z
|
||||
- "June 15" -> 2025-06-15T00:00:00.000Z
|
||||
|
||||
Return ONLY the timestamp - no explanations, no extra text.`,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'status',
|
||||
title: 'Status',
|
||||
type: 'dropdown',
|
||||
condition: { field: 'operation', value: 'create' },
|
||||
options: [
|
||||
{ label: 'Needs Action', id: 'needsAction' },
|
||||
{ label: 'Completed', id: 'completed' },
|
||||
],
|
||||
},
|
||||
|
||||
// Get/Update/Delete Task Fields - Task ID
|
||||
{
|
||||
id: 'taskId',
|
||||
title: 'Task ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Task ID',
|
||||
condition: { field: 'operation', value: ['get', 'update', 'delete'] },
|
||||
required: { field: 'operation', value: ['get', 'update', 'delete'] },
|
||||
},
|
||||
|
||||
// Update Task Fields
|
||||
{
|
||||
id: 'title',
|
||||
title: 'New Title',
|
||||
type: 'short-input',
|
||||
placeholder: 'Updated task title',
|
||||
condition: { field: 'operation', value: 'update' },
|
||||
},
|
||||
{
|
||||
id: 'notes',
|
||||
title: 'New Notes',
|
||||
type: 'long-input',
|
||||
placeholder: 'Updated task notes',
|
||||
condition: { field: 'operation', value: 'update' },
|
||||
},
|
||||
{
|
||||
id: 'due',
|
||||
title: 'New Due Date',
|
||||
type: 'short-input',
|
||||
placeholder: '2025-06-03T00:00:00.000Z',
|
||||
condition: { field: 'operation', value: 'update' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate an RFC 3339 timestamp in UTC based on the user's description.
|
||||
The timestamp should be in the format: YYYY-MM-DDTHH:MM:SS.000Z (UTC timezone).
|
||||
Examples:
|
||||
- "tomorrow" -> Calculate tomorrow's date at 00:00:00.000Z
|
||||
- "next Friday" -> Calculate the next Friday's date at 00:00:00.000Z
|
||||
- "June 15" -> 2025-06-15T00:00:00.000Z
|
||||
|
||||
Return ONLY the timestamp - no explanations, no extra text.`,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'status',
|
||||
title: 'New Status',
|
||||
type: 'dropdown',
|
||||
condition: { field: 'operation', value: 'update' },
|
||||
options: [
|
||||
{ label: 'Needs Action', id: 'needsAction' },
|
||||
{ label: 'Completed', id: 'completed' },
|
||||
],
|
||||
},
|
||||
|
||||
// List Tasks Fields
|
||||
{
|
||||
id: 'maxResults',
|
||||
title: 'Max Results',
|
||||
type: 'short-input',
|
||||
placeholder: '20',
|
||||
condition: { field: 'operation', value: ['list', 'list_task_lists'] },
|
||||
},
|
||||
{
|
||||
id: 'showCompleted',
|
||||
title: 'Show Completed',
|
||||
type: 'dropdown',
|
||||
condition: { field: 'operation', value: 'list' },
|
||||
options: [
|
||||
{ label: 'Yes', id: 'true' },
|
||||
{ label: 'No', id: 'false' },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
tools: {
|
||||
access: [
|
||||
'google_tasks_create',
|
||||
'google_tasks_list',
|
||||
'google_tasks_get',
|
||||
'google_tasks_update',
|
||||
'google_tasks_delete',
|
||||
'google_tasks_list_task_lists',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => {
|
||||
switch (params.operation) {
|
||||
case 'create':
|
||||
return 'google_tasks_create'
|
||||
case 'list':
|
||||
return 'google_tasks_list'
|
||||
case 'get':
|
||||
return 'google_tasks_get'
|
||||
case 'update':
|
||||
return 'google_tasks_update'
|
||||
case 'delete':
|
||||
return 'google_tasks_delete'
|
||||
case 'list_task_lists':
|
||||
return 'google_tasks_list_task_lists'
|
||||
default:
|
||||
throw new Error(`Invalid Google Tasks operation: ${params.operation}`)
|
||||
}
|
||||
},
|
||||
params: (params) => {
|
||||
const { oauthCredential, operation, showCompleted, maxResults, ...rest } = params
|
||||
|
||||
const processedParams: Record<string, unknown> = { ...rest }
|
||||
|
||||
if (maxResults && typeof maxResults === 'string') {
|
||||
processedParams.maxResults = Number.parseInt(maxResults, 10)
|
||||
}
|
||||
|
||||
if (showCompleted !== undefined) {
|
||||
processedParams.showCompleted = showCompleted === 'true'
|
||||
}
|
||||
|
||||
return {
|
||||
oauthCredential,
|
||||
...processedParams,
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'Operation to perform' },
|
||||
oauthCredential: { type: 'string', description: 'Google Tasks access token' },
|
||||
taskListId: { type: 'string', description: 'Task list identifier' },
|
||||
title: { type: 'string', description: 'Task title' },
|
||||
notes: { type: 'string', description: 'Task notes' },
|
||||
due: { type: 'string', description: 'Task due date' },
|
||||
status: { type: 'string', description: 'Task status' },
|
||||
taskId: { type: 'string', description: 'Task identifier' },
|
||||
maxResults: { type: 'string', description: 'Maximum number of results' },
|
||||
showCompleted: { type: 'string', description: 'Whether to show completed tasks' },
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Task ID' },
|
||||
title: { type: 'string', description: 'Task title' },
|
||||
notes: { type: 'string', description: 'Task notes' },
|
||||
status: { type: 'string', description: 'Task status' },
|
||||
due: { type: 'string', description: 'Due date' },
|
||||
updated: { type: 'string', description: 'Last modification time' },
|
||||
selfLink: { type: 'string', description: 'URL for the task' },
|
||||
webViewLink: { type: 'string', description: 'Link to task in Google Tasks UI' },
|
||||
parent: { type: 'string', description: 'Parent task ID' },
|
||||
position: { type: 'string', description: 'Position among sibling tasks' },
|
||||
completed: { type: 'string', description: 'Completion date' },
|
||||
deleted: { type: 'boolean', description: 'Whether the task is deleted' },
|
||||
tasks: { type: 'json', description: 'Array of tasks (list operation)' },
|
||||
taskLists: { type: 'json', description: 'Array of task lists (list_task_lists operation)' },
|
||||
taskId: { type: 'string', description: 'Deleted task ID (delete operation)' },
|
||||
nextPageToken: { type: 'string', description: 'Token for next page of results' },
|
||||
},
|
||||
}
|
||||
@@ -135,7 +135,7 @@ const SUPPORTED_LANGUAGES = [
|
||||
{ label: 'Yiddish', id: 'yi' },
|
||||
{ label: 'Yoruba', id: 'yo' },
|
||||
{ label: 'Zulu', id: 'zu' },
|
||||
] as const
|
||||
] satisfies { label: string; id: string }[]
|
||||
|
||||
export const GoogleTranslateBlock: BlockConfig = {
|
||||
type: 'google_translate',
|
||||
|
||||
@@ -23,6 +23,7 @@ import { ConditionBlock } from '@/blocks/blocks/condition'
|
||||
import { ConfluenceBlock, ConfluenceV2Block } from '@/blocks/blocks/confluence'
|
||||
import { CursorBlock, CursorV2Block } from '@/blocks/blocks/cursor'
|
||||
import { DatadogBlock } from '@/blocks/blocks/datadog'
|
||||
import { DevinBlock } from '@/blocks/blocks/devin'
|
||||
import { DiscordBlock } from '@/blocks/blocks/discord'
|
||||
import { DropboxBlock } from '@/blocks/blocks/dropbox'
|
||||
import { DSPyBlock } from '@/blocks/blocks/dspy'
|
||||
@@ -43,6 +44,7 @@ import { GitLabBlock } from '@/blocks/blocks/gitlab'
|
||||
import { GmailBlock, GmailV2Block } from '@/blocks/blocks/gmail'
|
||||
import { GongBlock } from '@/blocks/blocks/gong'
|
||||
import { GoogleSearchBlock } from '@/blocks/blocks/google'
|
||||
import { GoogleBigQueryBlock } from '@/blocks/blocks/google_bigquery'
|
||||
import { GoogleBooksBlock } from '@/blocks/blocks/google_books'
|
||||
import { GoogleCalendarBlock, GoogleCalendarV2Block } from '@/blocks/blocks/google_calendar'
|
||||
import { GoogleDocsBlock } from '@/blocks/blocks/google_docs'
|
||||
@@ -52,6 +54,7 @@ import { GoogleGroupsBlock } from '@/blocks/blocks/google_groups'
|
||||
import { GoogleMapsBlock } from '@/blocks/blocks/google_maps'
|
||||
import { GoogleSheetsBlock, GoogleSheetsV2Block } from '@/blocks/blocks/google_sheets'
|
||||
import { GoogleSlidesBlock, GoogleSlidesV2Block } from '@/blocks/blocks/google_slides'
|
||||
import { GoogleTasksBlock } from '@/blocks/blocks/google_tasks'
|
||||
import { GoogleTranslateBlock } from '@/blocks/blocks/google_translate'
|
||||
import { GoogleVaultBlock } from '@/blocks/blocks/google_vault'
|
||||
import { GrafanaBlock } from '@/blocks/blocks/grafana'
|
||||
@@ -204,6 +207,7 @@ export const registry: Record<string, BlockConfig> = {
|
||||
cursor: CursorBlock,
|
||||
cursor_v2: CursorV2Block,
|
||||
datadog: DatadogBlock,
|
||||
devin: DevinBlock,
|
||||
discord: DiscordBlock,
|
||||
dropbox: DropboxBlock,
|
||||
dspy: DSPyBlock,
|
||||
@@ -235,6 +239,7 @@ export const registry: Record<string, BlockConfig> = {
|
||||
google_forms: GoogleFormsBlock,
|
||||
google_groups: GoogleGroupsBlock,
|
||||
google_maps: GoogleMapsBlock,
|
||||
google_tasks: GoogleTasksBlock,
|
||||
google_translate: GoogleTranslateBlock,
|
||||
gong: GongBlock,
|
||||
google_search: GoogleSearchBlock,
|
||||
@@ -242,6 +247,7 @@ export const registry: Record<string, BlockConfig> = {
|
||||
google_sheets_v2: GoogleSheetsV2Block,
|
||||
google_slides: GoogleSlidesBlock,
|
||||
google_slides_v2: GoogleSlidesV2Block,
|
||||
google_bigquery: GoogleBigQueryBlock,
|
||||
google_vault: GoogleVaultBlock,
|
||||
grafana: GrafanaBlock,
|
||||
grain: GrainBlock,
|
||||
|
||||
@@ -939,6 +939,25 @@ export function GoogleIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function DevinIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 500 500' fill='none' xmlns='http://www.w3.org/2000/svg'>
|
||||
<path
|
||||
d='M59.29,209.39l48.87,28.21c1.75,1.01,3.71,1.51,5.67,1.51c1.95,0,3.92-0.52,5.67-1.51l48.87-28.21c0,0,0.14-0.11,0.2-0.16c0.74-0.45,1.44-0.99,2.07-1.6c0.09-0.09,0.18-0.2,0.27-0.29c0.54-0.58,1.03-1.21,1.44-1.89c0.06-0.11,0.16-0.2,0.2-0.32c0.43-0.74,0.74-1.53,0.99-2.37c0.05-0.18,0.09-0.36,0.14-0.54c0.2-0.86,0.36-1.74,0.36-2.66v-28.21c0-10.89,5.87-21.03,15.3-26.48c9.42-5.45,21.15-5.44,30.59,0l24.43,14.11c0.79,0.45,1.62,0.77,2.47,1.01c0.18,0.05,0.37,0.11,0.54,0.16c0.83,0.2,1.69,0.32,2.54,0.34c0.05,0,0.09,0,0.11,0c0.09,0,0.18-0.05,0.26-0.05c0.79,0,1.58-0.11,2.34-0.32c0.14-0.03,0.27-0.05,0.4-0.09c0.83-0.23,1.64-0.57,2.41-0.99c0.06-0.05,0.16-0.05,0.23-0.09l48.87-28.21c3.51-2.03,5.67-5.76,5.67-9.81V64.52c0-4.05-2.16-7.78-5.67-9.81l-48.91-28.19c-3.51-2.03-7.81-2.03-11.32,0l-48.87,28.21c0,0-0.14,0.11-0.2,0.16c-0.74,0.45-1.44,0.99-2.07,1.6c-0.09,0.09-0.18,0.2-0.27,0.29c-0.54,0.58-1.03,1.21-1.44,1.89c-0.06,0.11-0.16,0.2-0.2,0.31c-0.43,0.74-0.74,1.53-0.99,2.37c-0.05,0.18-0.09,0.36-0.14,0.54c-0.2,0.86-0.36,1.74-0.36,2.66v28.21c0,10.89-5.87,21.03-15.3,26.5c-9.42,5.44-21.15,5.44-30.59,0l-24.42-14.1c-0.79-0.45-1.63-0.77-2.47-1.01c-0.18-0.05-0.36-0.11-0.54-0.16c-0.84-0.2-1.69-0.31-2.55-0.34c-0.14,0-0.25,0-0.38,0c-0.81,0-1.6,0.11-2.37,0.31c-0.14,0.02-0.25,0.05-0.38,0.09c-0.82,0.23-1.63,0.57-2.4,1c-0.06,0.05-0.16,0.05-0.23,0.09l-48.84,28.24c-3.51,2.03-5.67,5.76-5.67,9.81v56.42c0,4.05,2.16,7.78,5.67,9.81C59.29,209.41,59.29,209.39,59.29,209.39z'
|
||||
fill='#2A6DCE'
|
||||
/>
|
||||
<path
|
||||
d='M325.46,223.49c9.42-5.44,21.15-5.44,30.59,0l24.43,14.11c0.79,0.45,1.62,0.77,2.47,1.01c0.18,0.05,0.36,0.11,0.54,0.16c0.83,0.2,1.69,0.31,2.54,0.34c0.05,0,0.09,0,0.11,0c0.09,0,0.18-0.03,0.26-0.05c0.79,0,1.58-0.11,2.34-0.31c0.14-0.03,0.27-0.05,0.4-0.09c0.83-0.23,1.62-0.57,2.41-0.99c0.06-0.05,0.16-0.05,0.25-0.09l48.87-28.21c3.51-2.03,5.67-5.76,5.67-9.81v-56.43c0-4.05-2.16-7.78-5.67-9.81l-48.84-28.22c-3.51-2.03-7.81-2.03-11.32,0l-48.87,28.21c0,0-0.14,0.11-0.2,0.16c-0.74,0.45-1.44,0.99-2.07,1.6c-0.09,0.09-0.18,0.2-0.26,0.29c-0.54,0.58-1.03,1.21-1.44,1.89c-0.06,0.11-0.16,0.2-0.2,0.32c-0.43,0.74-0.74,1.53-0.99,2.37c-0.05,0.18-0.09,0.36-0.14,0.54c-0.2,0.86-0.36,1.74-0.36,2.66v28.21c0,10.89-5.87,21.03-15.3,26.5c-9.42,5.44-21.15,5.44-30.59,0l-24.43-14.11c-0.79-0.45-1.62-0.77-2.47-1.01c-0.18-0.05-0.36-0.11-0.54-0.16c-0.83-0.2-1.69-0.32-2.54-0.34c-0.14,0-0.25,0-0.38,0c-0.81,0-1.6,0.11-2.37,0.32c-0.14,0.03-0.25,0.05-0.38,0.09c-0.83,0.23-1.64,0.57-2.41,0.99c-0.06,0.05-0.16,0.05-0.23,0.09l-48.87,28.21c-3.51,2.03-5.67,5.76-5.67,9.81v56.43c0,4.05,2.16,7.78,5.67,9.81l48.87,28.21c0,0,0.16,0.05,0.23,0.09c0.77,0.43,1.58,0.77,2.41,0.99c0.14,0.05,0.27,0.05,0.4,0.09c0.77,0.18,1.55,0.29,2.34,0.32c0.09,0,0.18,0.05,0.27,0.05c0.05,0,0.09,0,0.11,0c0.86,0,1.69-0.14,2.54-0.34c0.18-0.05,0.36-0.09,0.54-0.16c0.86-0.25,1.69-0.57,2.47-1.01l24.43-14.11c9.42-5.44,21.15-5.44,30.59,0c9.42,5.44,15.3,15.59,15.3,26.48v28.21c0,0.92,0.14,1.8,0.36,2.66c0.05,0.18,0.09,0.36,0.14,0.54c0.25,0.83,0.56,1.62,0.99,2.37c0.06,0.11,0.14,0.2,0.2,0.31c0.4,0.68,0.9,1.31,1.44,1.89c0.09,0.09,0.18,0.2,0.26,0.29c0.61,0.6,1.31,1.12,2.07,1.6c0.06,0.05,0.11,0.11,0.2,0.16l48.87,28.21c1.75,1.01,3.72,1.51,5.67,1.51s3.92-0.52,5.67-1.51l48.87-28.21c3.51-2.03,5.67-5.76,5.67-9.81v-56.43c0-4.05-2.16-7.78-5.67-9.81l-48.87-28.21c0,0-0.16-0.05-0.23-0.09c-0.77-0.43-1.58-0.77-2.41-0.99c-0.14-0.05-0.25-0.05-0.38-0.09c-0.79-0.18-1.57-0.29-2.38-0.32c-0.11,0-0.25,0-0.36,0c-0.86,0-1.71,0.14-2.54,0.34c-0.18,0.05-0.34,0.09-0.52,0.16c-0.86,0.25-1.69,0.57-2.47,1.01l-24.43,14.11c-9.42,5.44-21.15,5.44-30.58,0c-9.42-5.44-15.3-15.59-15.3-26.5c0-10.91,5.87-21.03,15.3-26.48C325.55,223.49,325.46,223.49,325.46,223.49z'
|
||||
fill='#1DC19C'
|
||||
/>
|
||||
<path
|
||||
d='M304.5,369.22l-48.87-28.21c0,0-0.16-0.05-0.23-0.09c-0.77-0.43-1.57-0.77-2.41-0.99c-0.14-0.05-0.27-0.05-0.4-0.09c-0.79-0.18-1.57-0.29-2.37-0.32c-0.14,0-0.25,0-0.38,0c-0.86,0-1.71,0.14-2.54,0.34c-0.18,0.05-0.34,0.09-0.52,0.16c-0.86,0.25-1.69,0.57-2.47,1.01l-24.43,14.11c-9.42,5.44-21.15,5.44-30.58,0c-9.42-5.44-15.3-15.59-15.3-26.5v-28.22c0-0.92-0.14-1.8-0.36-2.66c-0.05-0.18-0.09-0.36-0.14-0.54c-0.25-0.83-0.57-1.62-0.99-2.37c-0.06-0.11-0.14-0.2-0.2-0.32c-0.4-0.68-0.9-1.31-1.44-1.89c-0.09-0.09-0.18-0.2-0.27-0.29c-0.6-0.6-1.31-1.12-2.07-1.6c-0.06-0.05-0.11-0.11-0.2-0.16l-48.87-28.21c-3.51-2.03-7.81-2.03-11.32,0L59.28,290.6c-3.51,2.03-5.67,5.76-5.67,9.81v56.43c0,4.05,2.16,7.78,5.67,9.81l48.87,28.21c0,0,0.16,0.06,0.23,0.09c0.77,0.43,1.55,0.77,2.38,0.99c0.14,0.05,0.27,0.06,0.4,0.09c0.77,0.18,1.55,0.29,2.34,0.32c0.09,0,0.18,0.05,0.29,0.05c0.05,0,0.09,0,0.14,0c0.86,0,1.69-0.14,2.52-0.34c0.18-0.05,0.36-0.09,0.54-0.16c0.86-0.25,1.69-0.57,2.47-1.01l24.43-14.11c9.42-5.44,21.15-5.44,30.59,0c9.42,5.44,15.3,15.59,15.3,26.48v28.21c0,0.92,0.14,1.8,0.36,2.66c0.05,0.18,0.09,0.36,0.14,0.54c0.25,0.83,0.57,1.62,0.99,2.37c0.06,0.11,0.14,0.2,0.2,0.32c0.4,0.68,0.9,1.31,1.44,1.89c0.09,0.09,0.18,0.2,0.27,0.29c0.61,0.61,1.31,1.12,2.07,1.6c0.06,0.05,0.11,0.11,0.2,0.16l48.87,28.21c1.75,1.01,3.71,1.51,5.67,1.51c1.96,0,3.92-0.52,5.67-1.51l48.87-28.21c3.51-2.03,5.67-5.76,5.67-9.81v-56.43c0-4.05-2.16-7.78-5.67-9.81L304.5,369.22z'
|
||||
fill='#1796E2'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function DiscordIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
@@ -1302,6 +1321,21 @@ export function GoogleCalendarIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function GoogleTasksIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 527.1 500' xmlns='http://www.w3.org/2000/svg'>
|
||||
<polygon
|
||||
fill='#0066DA'
|
||||
points='410.4,58.3 368.8,81.2 348.2,120.6 368.8,168.8 407.8,211 450,187.5 475.9,142.8 450,87.5'
|
||||
/>
|
||||
<path
|
||||
fill='#2684FC'
|
||||
d='M249.3,219.4l98.9-98.9c29.1,22.1,50.5,53.8,59.6,90.4L272.1,346.7c-12.2,12.2-32,12.2-44.2,0l-91.5-91.5 c-9.8-9.8-9.8-25.6,0-35.3l39-39c9.8-9.8,25.6-9.8,35.3,0L249.3,219.4z M519.8,63.6l-39.7-39.7c-9.7-9.7-25.6-9.7-35.3,0 l-34.4,34.4c27.5,23,49.9,51.8,65.5,84.5l43.9-43.9C529.6,89.2,529.6,73.3,519.8,63.6z M412.5,250c0,89.8-72.8,162.5-162.5,162.5 S87.5,339.8,87.5,250S160.2,87.5,250,87.5c36.9,0,70.9,12.3,98.2,33.1l62.2-62.2C367,21.9,311.1,0,250,0C111.9,0,0,111.9,0,250 s111.9,250,250,250s250-111.9,250-250c0-38.3-8.7-74.7-24.1-107.2L407.8,211C410.8,223.5,412.5,236.6,412.5,250z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function SupabaseIcon(props: SVGProps<SVGSVGElement>) {
|
||||
const id = useId()
|
||||
const gradient0 = `supabase_paint0_${id}`
|
||||
@@ -3430,6 +3464,23 @@ export const ResendIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
</svg>
|
||||
)
|
||||
|
||||
export const GoogleBigQueryIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'>
|
||||
<path
|
||||
d='M14.48 58.196L.558 34.082c-.744-1.288-.744-2.876 0-4.164L14.48 5.805c.743-1.287 2.115-2.08 3.6-2.082h27.857c1.48.007 2.845.8 3.585 2.082l13.92 24.113c.744 1.288.744 2.876 0 4.164L49.52 58.196c-.743 1.287-2.115 2.08-3.6 2.082H18.07c-1.483-.005-2.85-.798-3.593-2.082z'
|
||||
fill='#4386fa'
|
||||
/>
|
||||
<path
|
||||
d='M40.697 24.235s3.87 9.283-1.406 14.545-14.883 1.894-14.883 1.894L43.95 60.27h1.984c1.486-.002 2.858-.796 3.6-2.082L58.75 42.23z'
|
||||
opacity='.1'
|
||||
/>
|
||||
<path
|
||||
d='M45.267 43.23L41 38.953a.67.67 0 0 0-.158-.12 11.63 11.63 0 1 0-2.032 2.037.67.67 0 0 0 .113.15l4.277 4.277a.67.67 0 0 0 .947 0l1.12-1.12a.67.67 0 0 0 0-.947zM31.64 40.464a8.75 8.75 0 1 1 8.749-8.749 8.75 8.75 0 0 1-8.749 8.749zm-5.593-9.216v3.616c.557.983 1.363 1.803 2.338 2.375v-6.013zm4.375-2.998v9.772a6.45 6.45 0 0 0 2.338 0V28.25zm6.764 6.606v-2.142H34.85v4.5a6.43 6.43 0 0 0 2.338-2.368z'
|
||||
fill='#fff'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export const GoogleVaultIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 82 82'>
|
||||
<path
|
||||
|
||||
@@ -80,7 +80,10 @@ export class BlockExecutor {
|
||||
const startTime = performance.now()
|
||||
let resolvedInputs: Record<string, any> = {}
|
||||
|
||||
const nodeMetadata = this.buildNodeMetadata(node)
|
||||
const nodeMetadata = {
|
||||
...this.buildNodeMetadata(node),
|
||||
executionOrder: blockLog?.executionOrder,
|
||||
}
|
||||
let cleanupSelfReference: (() => void) | undefined
|
||||
|
||||
if (block.metadata?.id === BlockType.HUMAN_IN_THE_LOOP) {
|
||||
|
||||
@@ -89,7 +89,8 @@ export interface ExecutionCallbacks {
|
||||
onChildWorkflowInstanceReady?: (
|
||||
blockId: string,
|
||||
childWorkflowInstanceId: string,
|
||||
iterationContext?: IterationContext
|
||||
iterationContext?: IterationContext,
|
||||
executionOrder?: number
|
||||
) => void
|
||||
}
|
||||
|
||||
@@ -155,7 +156,8 @@ export interface ContextExtensions {
|
||||
onChildWorkflowInstanceReady?: (
|
||||
blockId: string,
|
||||
childWorkflowInstanceId: string,
|
||||
iterationContext?: IterationContext
|
||||
iterationContext?: IterationContext,
|
||||
executionOrder?: number
|
||||
) => void
|
||||
|
||||
/**
|
||||
|
||||
@@ -62,6 +62,7 @@ export class WorkflowBlockHandler implements BlockHandler {
|
||||
branchTotal?: number
|
||||
originalBlockId?: string
|
||||
isLoopNode?: boolean
|
||||
executionOrder?: number
|
||||
}
|
||||
): Promise<BlockOutput | StreamingExecution> {
|
||||
return this._executeCore(ctx, block, inputs, nodeMetadata)
|
||||
@@ -79,6 +80,7 @@ export class WorkflowBlockHandler implements BlockHandler {
|
||||
branchTotal?: number
|
||||
originalBlockId?: string
|
||||
isLoopNode?: boolean
|
||||
executionOrder?: number
|
||||
}
|
||||
): Promise<BlockOutput | StreamingExecution> {
|
||||
logger.info(`Executing workflow block: ${block.id}`)
|
||||
@@ -169,7 +171,12 @@ export class WorkflowBlockHandler implements BlockHandler {
|
||||
const iterationContext = nodeMetadata
|
||||
? this.getIterationContext(ctx, nodeMetadata)
|
||||
: undefined
|
||||
ctx.onChildWorkflowInstanceReady?.(effectiveBlockId, instanceId, iterationContext)
|
||||
ctx.onChildWorkflowInstanceReady?.(
|
||||
effectiveBlockId,
|
||||
instanceId,
|
||||
iterationContext,
|
||||
nodeMetadata?.executionOrder
|
||||
)
|
||||
}
|
||||
|
||||
const subExecutor = new Executor({
|
||||
|
||||
@@ -264,7 +264,8 @@ export interface ExecutionContext {
|
||||
onChildWorkflowInstanceReady?: (
|
||||
blockId: string,
|
||||
childWorkflowInstanceId: string,
|
||||
iterationContext?: IterationContext
|
||||
iterationContext?: IterationContext,
|
||||
executionOrder?: number
|
||||
) => void
|
||||
|
||||
/**
|
||||
@@ -377,6 +378,7 @@ export interface BlockHandler {
|
||||
branchTotal?: number
|
||||
originalBlockId?: string
|
||||
isLoopNode?: boolean
|
||||
executionOrder?: number
|
||||
}
|
||||
) => Promise<BlockOutput | StreamingExecution>
|
||||
}
|
||||
|
||||
@@ -215,5 +215,115 @@ describe('start-block utilities', () => {
|
||||
|
||||
expect(output.customField).toBe('defaultValue')
|
||||
})
|
||||
|
||||
it.concurrent('preserves coerced types for unified start payload', () => {
|
||||
const block = createBlock('start_trigger', 'start', {
|
||||
subBlocks: {
|
||||
inputFormat: {
|
||||
value: [
|
||||
{ name: 'conversation_id', type: 'number' },
|
||||
{ name: 'sender', type: 'object' },
|
||||
{ name: 'is_active', type: 'boolean' },
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const resolution = {
|
||||
blockId: 'start',
|
||||
block,
|
||||
path: StartBlockPath.UNIFIED,
|
||||
} as const
|
||||
|
||||
const output = buildStartBlockOutput({
|
||||
resolution,
|
||||
workflowInput: {
|
||||
conversation_id: '149',
|
||||
sender: '{"id":10,"email":"user@example.com"}',
|
||||
is_active: 'true',
|
||||
},
|
||||
})
|
||||
|
||||
expect(output.conversation_id).toBe(149)
|
||||
expect(output.sender).toEqual({ id: 10, email: 'user@example.com' })
|
||||
expect(output.is_active).toBe(true)
|
||||
})
|
||||
|
||||
it.concurrent(
|
||||
'prefers coerced inputFormat values over duplicated top-level workflowInput keys',
|
||||
() => {
|
||||
const block = createBlock('start_trigger', 'start', {
|
||||
subBlocks: {
|
||||
inputFormat: {
|
||||
value: [
|
||||
{ name: 'conversation_id', type: 'number' },
|
||||
{ name: 'sender', type: 'object' },
|
||||
{ name: 'is_active', type: 'boolean' },
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const resolution = {
|
||||
blockId: 'start',
|
||||
block,
|
||||
path: StartBlockPath.UNIFIED,
|
||||
} as const
|
||||
|
||||
const output = buildStartBlockOutput({
|
||||
resolution,
|
||||
workflowInput: {
|
||||
input: {
|
||||
conversation_id: '149',
|
||||
sender: '{"id":10,"email":"user@example.com"}',
|
||||
is_active: 'false',
|
||||
},
|
||||
conversation_id: '150',
|
||||
sender: '{"id":99,"email":"wrong@example.com"}',
|
||||
is_active: 'true',
|
||||
extra: 'keep-me',
|
||||
},
|
||||
})
|
||||
|
||||
expect(output.conversation_id).toBe(149)
|
||||
expect(output.sender).toEqual({ id: 10, email: 'user@example.com' })
|
||||
expect(output.is_active).toBe(false)
|
||||
expect(output.extra).toBe('keep-me')
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
describe('EXTERNAL_TRIGGER path', () => {
|
||||
it.concurrent('preserves coerced types for integration trigger payload', () => {
|
||||
const block = createBlock('webhook', 'start', {
|
||||
subBlocks: {
|
||||
inputFormat: {
|
||||
value: [
|
||||
{ name: 'count', type: 'number' },
|
||||
{ name: 'payload', type: 'object' },
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const resolution = {
|
||||
blockId: 'start',
|
||||
block,
|
||||
path: StartBlockPath.EXTERNAL_TRIGGER,
|
||||
} as const
|
||||
|
||||
const output = buildStartBlockOutput({
|
||||
resolution,
|
||||
workflowInput: {
|
||||
count: '5',
|
||||
payload: '{"event":"push"}',
|
||||
extra: 'untouched',
|
||||
},
|
||||
})
|
||||
|
||||
expect(output.count).toBe(5)
|
||||
expect(output.payload).toEqual({ event: 'push' })
|
||||
expect(output.extra).toBe('untouched')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -262,6 +262,7 @@ function buildUnifiedStartOutput(
|
||||
hasStructured: boolean
|
||||
): NormalizedBlockOutput {
|
||||
const output: NormalizedBlockOutput = {}
|
||||
const structuredKeys = hasStructured ? new Set(Object.keys(structuredInput)) : null
|
||||
|
||||
if (hasStructured) {
|
||||
for (const [key, value] of Object.entries(structuredInput)) {
|
||||
@@ -272,6 +273,9 @@ function buildUnifiedStartOutput(
|
||||
if (isPlainObject(workflowInput)) {
|
||||
for (const [key, value] of Object.entries(workflowInput)) {
|
||||
if (key === 'onUploadError') continue
|
||||
// Skip keys already set by schema-coerced structuredInput to
|
||||
// prevent raw workflowInput strings from overwriting typed values.
|
||||
if (structuredKeys?.has(key)) continue
|
||||
// Runtime values override defaults (except undefined/null which mean "not provided")
|
||||
if (value !== undefined && value !== null) {
|
||||
output[key] = value
|
||||
@@ -384,6 +388,7 @@ function buildIntegrationTriggerOutput(
|
||||
hasStructured: boolean
|
||||
): NormalizedBlockOutput {
|
||||
const output: NormalizedBlockOutput = {}
|
||||
const structuredKeys = hasStructured ? new Set(Object.keys(structuredInput)) : null
|
||||
|
||||
if (hasStructured) {
|
||||
for (const [key, value] of Object.entries(structuredInput)) {
|
||||
@@ -393,6 +398,7 @@ function buildIntegrationTriggerOutput(
|
||||
|
||||
if (isPlainObject(workflowInput)) {
|
||||
for (const [key, value] of Object.entries(workflowInput)) {
|
||||
if (structuredKeys?.has(key)) continue
|
||||
if (value !== undefined && value !== null) {
|
||||
output[key] = value
|
||||
} else if (!Object.hasOwn(output, key)) {
|
||||
|
||||
@@ -484,8 +484,10 @@ export const auth = betterAuth({
|
||||
'google-docs',
|
||||
'google-sheets',
|
||||
'google-forms',
|
||||
'google-bigquery',
|
||||
'google-vault',
|
||||
'google-groups',
|
||||
'google-tasks',
|
||||
'vertex-ai',
|
||||
'github-repo',
|
||||
'microsoft-dataverse',
|
||||
@@ -1068,6 +1070,46 @@ export const auth = betterAuth({
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
providerId: 'google-bigquery',
|
||||
clientId: env.GOOGLE_CLIENT_ID as string,
|
||||
clientSecret: env.GOOGLE_CLIENT_SECRET as string,
|
||||
discoveryUrl: 'https://accounts.google.com/.well-known/openid-configuration',
|
||||
accessType: 'offline',
|
||||
scopes: [
|
||||
'https://www.googleapis.com/auth/userinfo.email',
|
||||
'https://www.googleapis.com/auth/userinfo.profile',
|
||||
'https://www.googleapis.com/auth/bigquery',
|
||||
],
|
||||
prompt: 'consent',
|
||||
redirectURI: `${getBaseUrl()}/api/auth/oauth2/callback/google-bigquery`,
|
||||
getUserInfo: async (tokens) => {
|
||||
try {
|
||||
const response = await fetch('https://openidconnect.googleapis.com/v1/userinfo', {
|
||||
headers: { Authorization: `Bearer ${tokens.accessToken}` },
|
||||
})
|
||||
if (!response.ok) {
|
||||
logger.error('Failed to fetch Google user info', { status: response.status })
|
||||
throw new Error(`Failed to fetch Google user info: ${response.statusText}`)
|
||||
}
|
||||
const profile = await response.json()
|
||||
const now = new Date()
|
||||
return {
|
||||
id: `${profile.sub}-${crypto.randomUUID()}`,
|
||||
name: profile.name || 'Google User',
|
||||
email: profile.email,
|
||||
image: profile.picture || undefined,
|
||||
emailVerified: profile.email_verified || false,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Error in Google getUserInfo', { error })
|
||||
throw error
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
providerId: 'google-vault',
|
||||
clientId: env.GOOGLE_CLIENT_ID as string,
|
||||
@@ -1150,6 +1192,46 @@ export const auth = betterAuth({
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
providerId: 'google-tasks',
|
||||
clientId: env.GOOGLE_CLIENT_ID as string,
|
||||
clientSecret: env.GOOGLE_CLIENT_SECRET as string,
|
||||
discoveryUrl: 'https://accounts.google.com/.well-known/openid-configuration',
|
||||
accessType: 'offline',
|
||||
scopes: [
|
||||
'https://www.googleapis.com/auth/userinfo.email',
|
||||
'https://www.googleapis.com/auth/userinfo.profile',
|
||||
'https://www.googleapis.com/auth/tasks',
|
||||
],
|
||||
prompt: 'consent',
|
||||
redirectURI: `${getBaseUrl()}/api/auth/oauth2/callback/google-tasks`,
|
||||
getUserInfo: async (tokens) => {
|
||||
try {
|
||||
const response = await fetch('https://openidconnect.googleapis.com/v1/userinfo', {
|
||||
headers: { Authorization: `Bearer ${tokens.accessToken}` },
|
||||
})
|
||||
if (!response.ok) {
|
||||
logger.error('Failed to fetch Google user info', { status: response.status })
|
||||
throw new Error(`Failed to fetch Google user info: ${response.statusText}`)
|
||||
}
|
||||
const profile = await response.json()
|
||||
const now = new Date()
|
||||
return {
|
||||
id: `${profile.sub}-${crypto.randomUUID()}`,
|
||||
name: profile.name || 'Google User',
|
||||
email: profile.email,
|
||||
image: profile.picture || undefined,
|
||||
emailVerified: profile.email_verified || false,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Error in Google getUserInfo', { error })
|
||||
throw error
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
providerId: 'vertex-ai',
|
||||
clientId: env.GOOGLE_CLIENT_ID as string,
|
||||
@@ -1846,6 +1928,15 @@ export const auth = betterAuth({
|
||||
'write:content.property:confluence',
|
||||
'read:hierarchical-content:confluence',
|
||||
'read:content.metadata:confluence',
|
||||
'read:user:confluence',
|
||||
'read:task:confluence',
|
||||
'write:task:confluence',
|
||||
'delete:blogpost:confluence',
|
||||
'write:space:confluence',
|
||||
'delete:space:confluence',
|
||||
'read:space.property:confluence',
|
||||
'write:space.property:confluence',
|
||||
'read:space.permission:confluence',
|
||||
],
|
||||
responseType: 'code',
|
||||
pkce: true,
|
||||
|
||||
@@ -1039,3 +1039,74 @@ export function validateGoogleCalendarId(
|
||||
|
||||
return { isValid: true, sanitized: value }
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a pagination cursor token
|
||||
*
|
||||
* Pagination cursors are opaque tokens returned by APIs (e.g., Confluence, Jira)
|
||||
* and passed back to get the next page. They are typically base64-encoded or
|
||||
* URL-safe strings. This validator ensures the cursor cannot contain characters
|
||||
* that could alter URL structure.
|
||||
*
|
||||
* @param value - The cursor token to validate
|
||||
* @param paramName - Name of the parameter for error messages
|
||||
* @param maxLength - Maximum length (default: 1024)
|
||||
* @returns ValidationResult
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* if (cursor) {
|
||||
* const result = validatePaginationCursor(cursor, 'cursor')
|
||||
* if (!result.isValid) {
|
||||
* return NextResponse.json({ error: result.error }, { status: 400 })
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function validatePaginationCursor(
|
||||
value: string | null | undefined,
|
||||
paramName = 'cursor',
|
||||
maxLength = 1024
|
||||
): ValidationResult {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return {
|
||||
isValid: false,
|
||||
error: `${paramName} is required`,
|
||||
}
|
||||
}
|
||||
|
||||
if (value.length > maxLength) {
|
||||
logger.warn('Pagination cursor exceeds maximum length', {
|
||||
paramName,
|
||||
length: value.length,
|
||||
maxLength,
|
||||
})
|
||||
return {
|
||||
isValid: false,
|
||||
error: `${paramName} exceeds maximum length of ${maxLength} characters`,
|
||||
}
|
||||
}
|
||||
|
||||
if (/[\x00-\x1f\x7f]/.test(value) || value.includes('%00')) {
|
||||
logger.warn('Pagination cursor contains control characters', { paramName })
|
||||
return {
|
||||
isValid: false,
|
||||
error: `${paramName} contains invalid characters`,
|
||||
}
|
||||
}
|
||||
|
||||
// Allow alphanumeric, base64 chars (+, /, =), and URL-safe chars (-, _, ., ~, %)
|
||||
const cursorPattern = /^[A-Za-z0-9+/=\-_.~%]+$/
|
||||
if (!cursorPattern.test(value)) {
|
||||
logger.warn('Pagination cursor contains disallowed characters', {
|
||||
paramName,
|
||||
value: value.substring(0, 100),
|
||||
})
|
||||
return {
|
||||
isValid: false,
|
||||
error: `${paramName} contains invalid characters`,
|
||||
}
|
||||
}
|
||||
|
||||
return { isValid: true, sanitized: value }
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
DropboxIcon,
|
||||
GithubIcon,
|
||||
GmailIcon,
|
||||
GoogleBigQueryIcon,
|
||||
GoogleCalendarIcon,
|
||||
GoogleDocsIcon,
|
||||
GoogleDriveIcon,
|
||||
@@ -15,6 +16,7 @@ import {
|
||||
GoogleGroupsIcon,
|
||||
GoogleIcon,
|
||||
GoogleSheetsIcon,
|
||||
GoogleTasksIcon,
|
||||
HubspotIcon,
|
||||
JiraIcon,
|
||||
LinearIcon,
|
||||
@@ -119,6 +121,22 @@ export const OAUTH_PROVIDERS: Record<string, OAuthProviderConfig> = {
|
||||
baseProviderIcon: GoogleIcon,
|
||||
scopes: ['https://www.googleapis.com/auth/calendar'],
|
||||
},
|
||||
'google-bigquery': {
|
||||
name: 'Google BigQuery',
|
||||
description: 'Query, list, and insert data in Google BigQuery.',
|
||||
providerId: 'google-bigquery',
|
||||
icon: GoogleBigQueryIcon,
|
||||
baseProviderIcon: GoogleIcon,
|
||||
scopes: ['https://www.googleapis.com/auth/bigquery'],
|
||||
},
|
||||
'google-tasks': {
|
||||
name: 'Google Tasks',
|
||||
description: 'Create, manage, and organize tasks with Google Tasks.',
|
||||
providerId: 'google-tasks',
|
||||
icon: GoogleTasksIcon,
|
||||
baseProviderIcon: GoogleIcon,
|
||||
scopes: ['https://www.googleapis.com/auth/tasks'],
|
||||
},
|
||||
'google-vault': {
|
||||
name: 'Google Vault',
|
||||
description: 'Search, export, and manage matters/holds via Google Vault.',
|
||||
@@ -330,6 +348,21 @@ export const OAUTH_PROVIDERS: Record<string, OAuthProviderConfig> = {
|
||||
'search:confluence',
|
||||
'read:me',
|
||||
'offline_access',
|
||||
'read:blogpost:confluence',
|
||||
'write:blogpost:confluence',
|
||||
'delete:blogpost:confluence',
|
||||
'read:content.property:confluence',
|
||||
'write:content.property:confluence',
|
||||
'read:hierarchical-content:confluence',
|
||||
'read:content.metadata:confluence',
|
||||
'read:user:confluence',
|
||||
'read:task:confluence',
|
||||
'write:task:confluence',
|
||||
'write:space:confluence',
|
||||
'delete:space:confluence',
|
||||
'read:space.property:confluence',
|
||||
'write:space.property:confluence',
|
||||
'read:space.permission:confluence',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
@@ -7,6 +7,8 @@ export type OAuthProvider =
|
||||
| 'google-docs'
|
||||
| 'google-sheets'
|
||||
| 'google-calendar'
|
||||
| 'google-bigquery'
|
||||
| 'google-tasks'
|
||||
| 'google-vault'
|
||||
| 'google-forms'
|
||||
| 'google-groups'
|
||||
@@ -52,6 +54,8 @@ export type OAuthService =
|
||||
| 'google-docs'
|
||||
| 'google-sheets'
|
||||
| 'google-calendar'
|
||||
| 'google-bigquery'
|
||||
| 'google-tasks'
|
||||
| 'google-vault'
|
||||
| 'google-forms'
|
||||
| 'google-groups'
|
||||
|
||||
@@ -155,6 +155,7 @@ export interface BlockChildWorkflowStartedEvent extends BaseExecutionEvent {
|
||||
childWorkflowInstanceId: string
|
||||
iterationCurrent?: number
|
||||
iterationContainerId?: string
|
||||
executionOrder?: number
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,7 +397,8 @@ export function createSSECallbacks(options: SSECallbackOptions) {
|
||||
const onChildWorkflowInstanceReady = (
|
||||
blockId: string,
|
||||
childWorkflowInstanceId: string,
|
||||
iterationContext?: IterationContext
|
||||
iterationContext?: IterationContext,
|
||||
executionOrder?: number
|
||||
) => {
|
||||
sendEvent({
|
||||
type: 'block:childWorkflowStarted',
|
||||
@@ -410,6 +412,7 @@ export function createSSECallbacks(options: SSECallbackOptions) {
|
||||
iterationCurrent: iterationContext.iterationCurrent,
|
||||
iterationContainerId: iterationContext.iterationContainerId,
|
||||
}),
|
||||
...(executionOrder !== undefined && { executionOrder }),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -270,9 +270,7 @@ async function auditWorkflowLockToggle(workflowId: string, actorId: string): Pro
|
||||
resourceType: AuditResourceType.WORKFLOW,
|
||||
resourceId: workflowId,
|
||||
resourceName: wf.name,
|
||||
description: allLocked
|
||||
? `Locked workflow "${wf.name}"`
|
||||
: `Unlocked workflow "${wf.name}"`,
|
||||
description: allLocked ? `Locked workflow "${wf.name}"` : `Unlocked workflow "${wf.name}"`,
|
||||
metadata: { blockCount: blocks.length },
|
||||
})
|
||||
}
|
||||
|
||||
@@ -91,6 +91,13 @@ const matchesEntryForUpdate = (
|
||||
return false
|
||||
}
|
||||
|
||||
if (
|
||||
update.childWorkflowBlockId !== undefined &&
|
||||
entry.childWorkflowBlockId !== update.childWorkflowBlockId
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
134
apps/sim/tools/confluence/create_space.ts
Normal file
134
apps/sim/tools/confluence/create_space.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
import { SPACE_DESCRIPTION_OUTPUT_PROPERTIES, TIMESTAMP_OUTPUT } from '@/tools/confluence/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export interface ConfluenceCreateSpaceParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
name: string
|
||||
key: string
|
||||
description?: string
|
||||
cloudId?: string
|
||||
}
|
||||
|
||||
export interface ConfluenceCreateSpaceResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
ts: string
|
||||
spaceId: string
|
||||
name: string
|
||||
key: string
|
||||
type: string
|
||||
status: string
|
||||
url: string
|
||||
homepageId: string | null
|
||||
description: { value: string; representation: string } | null
|
||||
}
|
||||
}
|
||||
|
||||
export const confluenceCreateSpaceTool: ToolConfig<
|
||||
ConfluenceCreateSpaceParams,
|
||||
ConfluenceCreateSpaceResponse
|
||||
> = {
|
||||
id: 'confluence_create_space',
|
||||
name: 'Confluence Create Space',
|
||||
description: 'Create a new Confluence space.',
|
||||
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)',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Name for the new space',
|
||||
},
|
||||
key: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique key for the space (uppercase, no spaces)',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Description for the new space',
|
||||
},
|
||||
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/space',
|
||||
method: 'POST',
|
||||
headers: (params: ConfluenceCreateSpaceParams) => ({
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
body: (params: ConfluenceCreateSpaceParams) => ({
|
||||
domain: params.domain,
|
||||
accessToken: params.accessToken,
|
||||
cloudId: params.cloudId,
|
||||
name: params.name,
|
||||
key: params.key,
|
||||
description: params.description,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
spaceId: data.id ?? '',
|
||||
name: data.name ?? '',
|
||||
key: data.key ?? '',
|
||||
type: data.type ?? '',
|
||||
status: data.status ?? '',
|
||||
url: data._links?.webui ?? '',
|
||||
homepageId: data.homepageId ?? null,
|
||||
description: data.description ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
spaceId: { type: 'string', description: 'Created space ID' },
|
||||
name: { type: 'string', description: 'Space name' },
|
||||
key: { type: 'string', description: 'Space key' },
|
||||
type: { type: 'string', description: 'Space type' },
|
||||
status: { type: 'string', description: 'Space status' },
|
||||
url: { type: 'string', description: 'URL to view the space' },
|
||||
homepageId: { type: 'string', description: 'Homepage ID', optional: true },
|
||||
description: {
|
||||
type: 'object',
|
||||
description: 'Space description',
|
||||
properties: SPACE_DESCRIPTION_OUTPUT_PROPERTIES,
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
118
apps/sim/tools/confluence/create_space_property.ts
Normal file
118
apps/sim/tools/confluence/create_space_property.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import { TIMESTAMP_OUTPUT } from '@/tools/confluence/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export interface ConfluenceCreateSpacePropertyParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
spaceId: string
|
||||
key: string
|
||||
value?: unknown
|
||||
cloudId?: string
|
||||
}
|
||||
|
||||
export interface ConfluenceCreateSpacePropertyResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
ts: string
|
||||
propertyId: string
|
||||
key: string
|
||||
value: unknown
|
||||
spaceId: string
|
||||
}
|
||||
}
|
||||
|
||||
export const confluenceCreateSpacePropertyTool: ToolConfig<
|
||||
ConfluenceCreateSpacePropertyParams,
|
||||
ConfluenceCreateSpacePropertyResponse
|
||||
> = {
|
||||
id: 'confluence_create_space_property',
|
||||
name: 'Confluence Create Space Property',
|
||||
description: 'Create a property on a Confluence space.',
|
||||
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)',
|
||||
},
|
||||
spaceId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Space ID to create the property on',
|
||||
},
|
||||
key: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Property key/name',
|
||||
},
|
||||
value: {
|
||||
type: 'json',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Property value (JSON)',
|
||||
},
|
||||
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/space-properties',
|
||||
method: 'POST',
|
||||
headers: (params: ConfluenceCreateSpacePropertyParams) => ({
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
body: (params: ConfluenceCreateSpacePropertyParams) => ({
|
||||
domain: params.domain,
|
||||
accessToken: params.accessToken,
|
||||
cloudId: params.cloudId,
|
||||
spaceId: params.spaceId,
|
||||
action: 'create',
|
||||
key: params.key,
|
||||
value: params.value,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
propertyId: data.propertyId ?? '',
|
||||
key: data.key ?? '',
|
||||
value: data.value ?? null,
|
||||
spaceId: data.spaceId ?? '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
propertyId: { type: 'string', description: 'Created property ID' },
|
||||
key: { type: 'string', description: 'Property key' },
|
||||
value: { type: 'json', description: 'Property value' },
|
||||
spaceId: { type: 'string', description: 'Space ID' },
|
||||
},
|
||||
}
|
||||
95
apps/sim/tools/confluence/delete_blogpost.ts
Normal file
95
apps/sim/tools/confluence/delete_blogpost.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { TIMESTAMP_OUTPUT } from '@/tools/confluence/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export interface ConfluenceDeleteBlogPostParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
blogPostId: string
|
||||
cloudId?: string
|
||||
}
|
||||
|
||||
export interface ConfluenceDeleteBlogPostResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
ts: string
|
||||
blogPostId: string
|
||||
deleted: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export const confluenceDeleteBlogPostTool: ToolConfig<
|
||||
ConfluenceDeleteBlogPostParams,
|
||||
ConfluenceDeleteBlogPostResponse
|
||||
> = {
|
||||
id: 'confluence_delete_blogpost',
|
||||
name: 'Confluence Delete Blog Post',
|
||||
description: 'Delete a Confluence blog post.',
|
||||
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)',
|
||||
},
|
||||
blogPostId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the blog post to delete',
|
||||
},
|
||||
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/blogposts',
|
||||
method: 'DELETE',
|
||||
headers: (params: ConfluenceDeleteBlogPostParams) => ({
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
body: (params: ConfluenceDeleteBlogPostParams) => ({
|
||||
domain: params.domain,
|
||||
accessToken: params.accessToken,
|
||||
cloudId: params.cloudId,
|
||||
blogPostId: params.blogPostId,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
blogPostId: data.blogPostId ?? '',
|
||||
deleted: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
blogPostId: { type: 'string', description: 'Deleted blog post ID' },
|
||||
deleted: { type: 'boolean', description: 'Deletion status' },
|
||||
},
|
||||
}
|
||||
95
apps/sim/tools/confluence/delete_space.ts
Normal file
95
apps/sim/tools/confluence/delete_space.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { TIMESTAMP_OUTPUT } from '@/tools/confluence/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export interface ConfluenceDeleteSpaceParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
spaceId: string
|
||||
cloudId?: string
|
||||
}
|
||||
|
||||
export interface ConfluenceDeleteSpaceResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
ts: string
|
||||
spaceId: string
|
||||
deleted: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export const confluenceDeleteSpaceTool: ToolConfig<
|
||||
ConfluenceDeleteSpaceParams,
|
||||
ConfluenceDeleteSpaceResponse
|
||||
> = {
|
||||
id: 'confluence_delete_space',
|
||||
name: 'Confluence Delete Space',
|
||||
description: 'Delete a Confluence space.',
|
||||
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)',
|
||||
},
|
||||
spaceId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the space to delete',
|
||||
},
|
||||
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/space',
|
||||
method: 'DELETE',
|
||||
headers: (params: ConfluenceDeleteSpaceParams) => ({
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
body: (params: ConfluenceDeleteSpaceParams) => ({
|
||||
domain: params.domain,
|
||||
accessToken: params.accessToken,
|
||||
cloudId: params.cloudId,
|
||||
spaceId: params.spaceId,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
spaceId: data.spaceId ?? '',
|
||||
deleted: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
spaceId: { type: 'string', description: 'Deleted space ID' },
|
||||
deleted: { type: 'boolean', description: 'Deletion status' },
|
||||
},
|
||||
}
|
||||
107
apps/sim/tools/confluence/delete_space_property.ts
Normal file
107
apps/sim/tools/confluence/delete_space_property.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import { TIMESTAMP_OUTPUT } from '@/tools/confluence/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export interface ConfluenceDeleteSpacePropertyParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
spaceId: string
|
||||
propertyId: string
|
||||
cloudId?: string
|
||||
}
|
||||
|
||||
export interface ConfluenceDeleteSpacePropertyResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
ts: string
|
||||
spaceId: string
|
||||
propertyId: string
|
||||
deleted: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export const confluenceDeleteSpacePropertyTool: ToolConfig<
|
||||
ConfluenceDeleteSpacePropertyParams,
|
||||
ConfluenceDeleteSpacePropertyResponse
|
||||
> = {
|
||||
id: 'confluence_delete_space_property',
|
||||
name: 'Confluence Delete Space Property',
|
||||
description: 'Delete a property from a Confluence space.',
|
||||
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)',
|
||||
},
|
||||
spaceId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Space ID the property belongs to',
|
||||
},
|
||||
propertyId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Property ID to delete',
|
||||
},
|
||||
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/space-properties',
|
||||
method: 'POST',
|
||||
headers: (params: ConfluenceDeleteSpacePropertyParams) => ({
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
body: (params: ConfluenceDeleteSpacePropertyParams) => ({
|
||||
domain: params.domain,
|
||||
accessToken: params.accessToken,
|
||||
cloudId: params.cloudId,
|
||||
spaceId: params.spaceId,
|
||||
action: 'delete',
|
||||
propertyId: params.propertyId,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
spaceId: data.spaceId ?? '',
|
||||
propertyId: data.propertyId ?? '',
|
||||
deleted: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
spaceId: { type: 'string', description: 'Space ID' },
|
||||
propertyId: { type: 'string', description: 'Deleted property ID' },
|
||||
deleted: { type: 'boolean', description: 'Deletion status' },
|
||||
},
|
||||
}
|
||||
147
apps/sim/tools/confluence/get_page_descendants.ts
Normal file
147
apps/sim/tools/confluence/get_page_descendants.ts
Normal file
@@ -0,0 +1,147 @@
|
||||
import { TIMESTAMP_OUTPUT } from '@/tools/confluence/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export interface ConfluenceGetPageDescendantsParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
pageId: string
|
||||
limit?: number
|
||||
cursor?: string
|
||||
cloudId?: string
|
||||
}
|
||||
|
||||
export interface ConfluenceGetPageDescendantsResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
ts: string
|
||||
descendants: Array<{
|
||||
id: string
|
||||
title: string
|
||||
type: string | null
|
||||
status: string | null
|
||||
spaceId: string | null
|
||||
parentId: string | null
|
||||
childPosition: number | null
|
||||
depth: number | null
|
||||
}>
|
||||
pageId: string
|
||||
nextCursor: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export const confluenceGetPageDescendantsTool: ToolConfig<
|
||||
ConfluenceGetPageDescendantsParams,
|
||||
ConfluenceGetPageDescendantsResponse
|
||||
> = {
|
||||
id: 'confluence_get_page_descendants',
|
||||
name: 'Confluence Get Page Descendants',
|
||||
description: 'Get all descendants of a Confluence page recursively.',
|
||||
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: 'Page ID to get descendants for',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of descendants to return (default: 50, max: 250)',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination cursor from previous response',
|
||||
},
|
||||
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/page-descendants',
|
||||
method: 'POST',
|
||||
headers: (params: ConfluenceGetPageDescendantsParams) => ({
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
body: (params: ConfluenceGetPageDescendantsParams) => ({
|
||||
domain: params.domain,
|
||||
accessToken: params.accessToken,
|
||||
cloudId: params.cloudId,
|
||||
pageId: params.pageId,
|
||||
limit: params.limit,
|
||||
cursor: params.cursor,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
descendants: data.descendants || [],
|
||||
pageId: data.pageId ?? '',
|
||||
nextCursor: data.nextCursor ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
descendants: {
|
||||
type: 'array',
|
||||
description: 'Array of descendant pages',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Page ID' },
|
||||
title: { type: 'string', description: 'Page title' },
|
||||
type: {
|
||||
type: 'string',
|
||||
description: 'Content type (page, whiteboard, database, etc.)',
|
||||
optional: true,
|
||||
},
|
||||
status: { type: 'string', description: 'Page status', optional: true },
|
||||
spaceId: { type: 'string', description: 'Space ID', optional: true },
|
||||
parentId: { type: 'string', description: 'Parent page ID', optional: true },
|
||||
childPosition: { type: 'number', description: 'Position among siblings', optional: true },
|
||||
depth: { type: 'number', description: 'Depth in the hierarchy', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
pageId: { type: 'string', description: 'Parent page ID' },
|
||||
nextCursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for fetching the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
130
apps/sim/tools/confluence/get_task.ts
Normal file
130
apps/sim/tools/confluence/get_task.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import { TIMESTAMP_OUTPUT } from '@/tools/confluence/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export interface ConfluenceGetTaskParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
taskId: string
|
||||
cloudId?: string
|
||||
}
|
||||
|
||||
export interface ConfluenceGetTaskResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
ts: string
|
||||
id: string
|
||||
localId: string | null
|
||||
spaceId: string | null
|
||||
pageId: string | null
|
||||
blogPostId: string | null
|
||||
status: string
|
||||
body: string | null
|
||||
createdBy: string | null
|
||||
assignedTo: string | null
|
||||
completedBy: string | null
|
||||
createdAt: string | null
|
||||
updatedAt: string | null
|
||||
dueAt: string | null
|
||||
completedAt: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export const confluenceGetTaskTool: ToolConfig<ConfluenceGetTaskParams, ConfluenceGetTaskResponse> =
|
||||
{
|
||||
id: 'confluence_get_task',
|
||||
name: 'Confluence Get Task',
|
||||
description: 'Get a specific Confluence inline task by ID.',
|
||||
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)',
|
||||
},
|
||||
taskId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the task to retrieve',
|
||||
},
|
||||
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/tasks',
|
||||
method: 'POST',
|
||||
headers: (params: ConfluenceGetTaskParams) => ({
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
body: (params: ConfluenceGetTaskParams) => ({
|
||||
domain: params.domain,
|
||||
accessToken: params.accessToken,
|
||||
cloudId: params.cloudId,
|
||||
taskId: params.taskId,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
const task = data.task || data
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
id: task.id ?? '',
|
||||
localId: task.localId ?? null,
|
||||
spaceId: task.spaceId ?? null,
|
||||
pageId: task.pageId ?? null,
|
||||
blogPostId: task.blogPostId ?? null,
|
||||
status: task.status ?? '',
|
||||
body: task.body ?? null,
|
||||
createdBy: task.createdBy ?? null,
|
||||
assignedTo: task.assignedTo ?? null,
|
||||
completedBy: task.completedBy ?? null,
|
||||
createdAt: task.createdAt ?? null,
|
||||
updatedAt: task.updatedAt ?? null,
|
||||
dueAt: task.dueAt ?? null,
|
||||
completedAt: task.completedAt ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
id: { type: 'string', description: 'Task ID' },
|
||||
localId: { type: 'string', description: 'Local task ID', optional: true },
|
||||
spaceId: { type: 'string', description: 'Space ID', optional: true },
|
||||
pageId: { type: 'string', description: 'Page ID', optional: true },
|
||||
blogPostId: { type: 'string', description: 'Blog post ID', optional: true },
|
||||
status: { type: 'string', description: 'Task status (complete or incomplete)' },
|
||||
body: { type: 'string', description: 'Task body content in storage format', optional: true },
|
||||
createdBy: { type: 'string', description: 'Creator account ID', optional: true },
|
||||
assignedTo: { type: 'string', description: 'Assignee account ID', optional: true },
|
||||
completedBy: { type: 'string', description: 'Completer account ID', optional: true },
|
||||
createdAt: { type: 'string', description: 'Creation timestamp', optional: true },
|
||||
updatedAt: { type: 'string', description: 'Last update timestamp', optional: true },
|
||||
dueAt: { type: 'string', description: 'Due date', optional: true },
|
||||
completedAt: { type: 'string', description: 'Completion timestamp', optional: true },
|
||||
},
|
||||
}
|
||||
113
apps/sim/tools/confluence/get_user.ts
Normal file
113
apps/sim/tools/confluence/get_user.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import { TIMESTAMP_OUTPUT } from '@/tools/confluence/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export interface ConfluenceGetUserParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
accountId: string
|
||||
cloudId?: string
|
||||
}
|
||||
|
||||
export interface ConfluenceGetUserResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
ts: string
|
||||
accountId: string
|
||||
displayName: string
|
||||
email: string | null
|
||||
accountType: string | null
|
||||
profilePicture: string | null
|
||||
publicName: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export const confluenceGetUserTool: ToolConfig<ConfluenceGetUserParams, ConfluenceGetUserResponse> =
|
||||
{
|
||||
id: 'confluence_get_user',
|
||||
name: 'Confluence Get User',
|
||||
description: 'Get display name and profile info for a Confluence user by account ID.',
|
||||
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)',
|
||||
},
|
||||
accountId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The Atlassian account ID of the user to look up',
|
||||
},
|
||||
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/user',
|
||||
method: 'POST',
|
||||
headers: (params: ConfluenceGetUserParams) => ({
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
body: (params: ConfluenceGetUserParams) => ({
|
||||
domain: params.domain,
|
||||
accessToken: params.accessToken,
|
||||
accountId: params.accountId?.trim(),
|
||||
cloudId: params.cloudId,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
accountId: data.accountId ?? '',
|
||||
displayName: data.displayName ?? '',
|
||||
email: data.email ?? null,
|
||||
accountType: data.accountType ?? null,
|
||||
profilePicture: data.profilePicture?.path ?? null,
|
||||
publicName: data.publicName ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
accountId: { type: 'string', description: 'Atlassian account ID of the user' },
|
||||
displayName: { type: 'string', description: 'Display name of the user' },
|
||||
email: { type: 'string', description: 'Email address of the user', optional: true },
|
||||
accountType: {
|
||||
type: 'string',
|
||||
description: 'Account type (e.g., atlassian, app, customer)',
|
||||
optional: true,
|
||||
},
|
||||
profilePicture: {
|
||||
type: 'string',
|
||||
description: 'Path to the user profile picture',
|
||||
optional: true,
|
||||
},
|
||||
publicName: { type: 'string', description: 'Public name of the user', optional: true },
|
||||
},
|
||||
}
|
||||
@@ -3,17 +3,25 @@ import { confluenceCreateBlogPostTool } from '@/tools/confluence/create_blogpost
|
||||
import { confluenceCreateCommentTool } from '@/tools/confluence/create_comment'
|
||||
import { confluenceCreatePageTool } from '@/tools/confluence/create_page'
|
||||
import { confluenceCreatePagePropertyTool } from '@/tools/confluence/create_page_property'
|
||||
import { confluenceCreateSpaceTool } from '@/tools/confluence/create_space'
|
||||
import { confluenceCreateSpacePropertyTool } from '@/tools/confluence/create_space_property'
|
||||
import { confluenceDeleteAttachmentTool } from '@/tools/confluence/delete_attachment'
|
||||
import { confluenceDeleteBlogPostTool } from '@/tools/confluence/delete_blogpost'
|
||||
import { confluenceDeleteCommentTool } from '@/tools/confluence/delete_comment'
|
||||
import { confluenceDeleteLabelTool } from '@/tools/confluence/delete_label'
|
||||
import { confluenceDeletePageTool } from '@/tools/confluence/delete_page'
|
||||
import { confluenceDeletePagePropertyTool } from '@/tools/confluence/delete_page_property'
|
||||
import { confluenceDeleteSpaceTool } from '@/tools/confluence/delete_space'
|
||||
import { confluenceDeleteSpacePropertyTool } from '@/tools/confluence/delete_space_property'
|
||||
import { confluenceGetBlogPostTool } from '@/tools/confluence/get_blogpost'
|
||||
import { confluenceGetPageAncestorsTool } from '@/tools/confluence/get_page_ancestors'
|
||||
import { confluenceGetPageChildrenTool } from '@/tools/confluence/get_page_children'
|
||||
import { confluenceGetPageDescendantsTool } from '@/tools/confluence/get_page_descendants'
|
||||
import { confluenceGetPageVersionTool } from '@/tools/confluence/get_page_version'
|
||||
import { confluenceGetPagesByLabelTool } from '@/tools/confluence/get_pages_by_label'
|
||||
import { confluenceGetSpaceTool } from '@/tools/confluence/get_space'
|
||||
import { confluenceGetTaskTool } from '@/tools/confluence/get_task'
|
||||
import { confluenceGetUserTool } from '@/tools/confluence/get_user'
|
||||
import { confluenceListAttachmentsTool } from '@/tools/confluence/list_attachments'
|
||||
import { confluenceListBlogPostsTool } from '@/tools/confluence/list_blogposts'
|
||||
import { confluenceListBlogPostsInSpaceTool } from '@/tools/confluence/list_blogposts_in_space'
|
||||
@@ -23,7 +31,10 @@ import { confluenceListPagePropertiesTool } from '@/tools/confluence/list_page_p
|
||||
import { confluenceListPageVersionsTool } from '@/tools/confluence/list_page_versions'
|
||||
import { confluenceListPagesInSpaceTool } from '@/tools/confluence/list_pages_in_space'
|
||||
import { confluenceListSpaceLabelsTool } from '@/tools/confluence/list_space_labels'
|
||||
import { confluenceListSpacePermissionsTool } from '@/tools/confluence/list_space_permissions'
|
||||
import { confluenceListSpacePropertiesTool } from '@/tools/confluence/list_space_properties'
|
||||
import { confluenceListSpacesTool } from '@/tools/confluence/list_spaces'
|
||||
import { confluenceListTasksTool } from '@/tools/confluence/list_tasks'
|
||||
import { confluenceRetrieveTool } from '@/tools/confluence/retrieve'
|
||||
import { confluenceSearchTool } from '@/tools/confluence/search'
|
||||
import { confluenceSearchInSpaceTool } from '@/tools/confluence/search_in_space'
|
||||
@@ -64,7 +75,10 @@ import {
|
||||
VERSION_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/confluence/types'
|
||||
import { confluenceUpdateTool } from '@/tools/confluence/update'
|
||||
import { confluenceUpdateBlogPostTool } from '@/tools/confluence/update_blogpost'
|
||||
import { confluenceUpdateCommentTool } from '@/tools/confluence/update_comment'
|
||||
import { confluenceUpdateSpaceTool } from '@/tools/confluence/update_space'
|
||||
import { confluenceUpdateTaskTool } from '@/tools/confluence/update_task'
|
||||
import { confluenceUploadAttachmentTool } from '@/tools/confluence/upload_attachment'
|
||||
|
||||
export {
|
||||
@@ -76,6 +90,7 @@ export {
|
||||
confluenceListPagesInSpaceTool,
|
||||
confluenceGetPageChildrenTool,
|
||||
confluenceGetPageAncestorsTool,
|
||||
confluenceGetPageDescendantsTool,
|
||||
// Page Version Tools
|
||||
confluenceListPageVersionsTool,
|
||||
confluenceGetPageVersionTool,
|
||||
@@ -87,6 +102,8 @@ export {
|
||||
confluenceListBlogPostsTool,
|
||||
confluenceGetBlogPostTool,
|
||||
confluenceCreateBlogPostTool,
|
||||
confluenceUpdateBlogPostTool,
|
||||
confluenceDeleteBlogPostTool,
|
||||
confluenceListBlogPostsInSpaceTool,
|
||||
// Search Tools
|
||||
confluenceSearchTool,
|
||||
@@ -106,9 +123,24 @@ export {
|
||||
confluenceDeleteLabelTool,
|
||||
confluenceGetPagesByLabelTool,
|
||||
confluenceListSpaceLabelsTool,
|
||||
// User Tools
|
||||
confluenceGetUserTool,
|
||||
// Space Tools
|
||||
confluenceGetSpaceTool,
|
||||
confluenceCreateSpaceTool,
|
||||
confluenceUpdateSpaceTool,
|
||||
confluenceDeleteSpaceTool,
|
||||
confluenceListSpacesTool,
|
||||
// Space Property Tools
|
||||
confluenceListSpacePropertiesTool,
|
||||
confluenceCreateSpacePropertyTool,
|
||||
confluenceDeleteSpacePropertyTool,
|
||||
// Space Permission Tools
|
||||
confluenceListSpacePermissionsTool,
|
||||
// Task Tools
|
||||
confluenceListTasksTool,
|
||||
confluenceGetTaskTool,
|
||||
confluenceUpdateTaskTool,
|
||||
// Item property constants (for use in outputs)
|
||||
ATTACHMENT_ITEM_PROPERTIES,
|
||||
COMMENT_ITEM_PROPERTIES,
|
||||
|
||||
156
apps/sim/tools/confluence/list_space_permissions.ts
Normal file
156
apps/sim/tools/confluence/list_space_permissions.ts
Normal file
@@ -0,0 +1,156 @@
|
||||
import { TIMESTAMP_OUTPUT } from '@/tools/confluence/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export interface ConfluenceListSpacePermissionsParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
spaceId: string
|
||||
limit?: number
|
||||
cursor?: string
|
||||
cloudId?: string
|
||||
}
|
||||
|
||||
export interface ConfluenceListSpacePermissionsResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
ts: string
|
||||
permissions: Array<{
|
||||
id: string
|
||||
principalType: string | null
|
||||
principalId: string | null
|
||||
operationKey: string | null
|
||||
operationTargetType: string | null
|
||||
anonymousAccess: boolean
|
||||
unlicensedAccess: boolean
|
||||
}>
|
||||
spaceId: string
|
||||
nextCursor: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export const confluenceListSpacePermissionsTool: ToolConfig<
|
||||
ConfluenceListSpacePermissionsParams,
|
||||
ConfluenceListSpacePermissionsResponse
|
||||
> = {
|
||||
id: 'confluence_list_space_permissions',
|
||||
name: 'Confluence List Space Permissions',
|
||||
description: 'List permissions for a Confluence space.',
|
||||
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)',
|
||||
},
|
||||
spaceId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Space ID to list permissions for',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of permissions to return (default: 50, max: 250)',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination cursor from previous response',
|
||||
},
|
||||
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/space-permissions',
|
||||
method: 'POST',
|
||||
headers: (params: ConfluenceListSpacePermissionsParams) => ({
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
body: (params: ConfluenceListSpacePermissionsParams) => ({
|
||||
domain: params.domain,
|
||||
accessToken: params.accessToken,
|
||||
cloudId: params.cloudId,
|
||||
spaceId: params.spaceId,
|
||||
limit: params.limit,
|
||||
cursor: params.cursor,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
permissions: data.permissions || [],
|
||||
spaceId: data.spaceId ?? '',
|
||||
nextCursor: data.nextCursor ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
permissions: {
|
||||
type: 'array',
|
||||
description: 'Array of space permissions',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Permission ID' },
|
||||
principalType: {
|
||||
type: 'string',
|
||||
description: 'Principal type (user, group, role)',
|
||||
optional: true,
|
||||
},
|
||||
principalId: { type: 'string', description: 'Principal ID', optional: true },
|
||||
operationKey: {
|
||||
type: 'string',
|
||||
description: 'Operation key (read, create, delete, etc.)',
|
||||
optional: true,
|
||||
},
|
||||
operationTargetType: {
|
||||
type: 'string',
|
||||
description: 'Target type (page, blogpost, space, etc.)',
|
||||
optional: true,
|
||||
},
|
||||
anonymousAccess: { type: 'boolean', description: 'Whether anonymous access is allowed' },
|
||||
unlicensedAccess: {
|
||||
type: 'boolean',
|
||||
description: 'Whether unlicensed access is allowed',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
spaceId: { type: 'string', description: 'Space ID' },
|
||||
nextCursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for fetching the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
133
apps/sim/tools/confluence/list_space_properties.ts
Normal file
133
apps/sim/tools/confluence/list_space_properties.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
import { TIMESTAMP_OUTPUT } from '@/tools/confluence/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export interface ConfluenceListSpacePropertiesParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
spaceId: string
|
||||
limit?: number
|
||||
cursor?: string
|
||||
cloudId?: string
|
||||
}
|
||||
|
||||
export interface ConfluenceListSpacePropertiesResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
ts: string
|
||||
properties: Array<{
|
||||
id: string
|
||||
key: string
|
||||
value: unknown
|
||||
}>
|
||||
spaceId: string
|
||||
nextCursor: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export const confluenceListSpacePropertiesTool: ToolConfig<
|
||||
ConfluenceListSpacePropertiesParams,
|
||||
ConfluenceListSpacePropertiesResponse
|
||||
> = {
|
||||
id: 'confluence_list_space_properties',
|
||||
name: 'Confluence List Space Properties',
|
||||
description: 'List properties on a Confluence space.',
|
||||
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)',
|
||||
},
|
||||
spaceId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Space ID to list properties for',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of properties to return (default: 50, max: 250)',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination cursor from previous response',
|
||||
},
|
||||
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/space-properties',
|
||||
method: 'POST',
|
||||
headers: (params: ConfluenceListSpacePropertiesParams) => ({
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
body: (params: ConfluenceListSpacePropertiesParams) => ({
|
||||
domain: params.domain,
|
||||
accessToken: params.accessToken,
|
||||
cloudId: params.cloudId,
|
||||
spaceId: params.spaceId,
|
||||
limit: params.limit,
|
||||
cursor: params.cursor,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
properties: data.properties || [],
|
||||
spaceId: data.spaceId ?? '',
|
||||
nextCursor: data.nextCursor ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
properties: {
|
||||
type: 'array',
|
||||
description: 'Array of space properties',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Property ID' },
|
||||
key: { type: 'string', description: 'Property key' },
|
||||
value: { type: 'json', description: 'Property value' },
|
||||
},
|
||||
},
|
||||
},
|
||||
spaceId: { type: 'string', description: 'Space ID' },
|
||||
nextCursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for fetching the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
181
apps/sim/tools/confluence/list_tasks.ts
Normal file
181
apps/sim/tools/confluence/list_tasks.ts
Normal file
@@ -0,0 +1,181 @@
|
||||
import { TIMESTAMP_OUTPUT } from '@/tools/confluence/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export interface ConfluenceListTasksParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
pageId?: string
|
||||
spaceId?: string
|
||||
assignedTo?: string
|
||||
status?: string
|
||||
limit?: number
|
||||
cursor?: string
|
||||
cloudId?: string
|
||||
}
|
||||
|
||||
export interface ConfluenceListTasksResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
ts: string
|
||||
tasks: Array<{
|
||||
id: string
|
||||
localId: string | null
|
||||
spaceId: string | null
|
||||
pageId: string | null
|
||||
blogPostId: string | null
|
||||
status: string
|
||||
body: string | null
|
||||
createdBy: string | null
|
||||
assignedTo: string | null
|
||||
completedBy: string | null
|
||||
createdAt: string | null
|
||||
updatedAt: string | null
|
||||
dueAt: string | null
|
||||
completedAt: string | null
|
||||
}>
|
||||
nextCursor: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export const confluenceListTasksTool: ToolConfig<
|
||||
ConfluenceListTasksParams,
|
||||
ConfluenceListTasksResponse
|
||||
> = {
|
||||
id: 'confluence_list_tasks',
|
||||
name: 'Confluence List Tasks',
|
||||
description:
|
||||
'List inline tasks from Confluence. Optionally filter by page, space, assignee, or status.',
|
||||
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: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter tasks by page ID',
|
||||
},
|
||||
spaceId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter tasks by space ID',
|
||||
},
|
||||
assignedTo: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter tasks by assignee account ID',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter tasks by status (complete or incomplete)',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of tasks to return (default: 50, max: 250)',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination cursor from previous response',
|
||||
},
|
||||
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/tasks',
|
||||
method: 'POST',
|
||||
headers: (params: ConfluenceListTasksParams) => ({
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
body: (params: ConfluenceListTasksParams) => ({
|
||||
domain: params.domain,
|
||||
accessToken: params.accessToken,
|
||||
cloudId: params.cloudId,
|
||||
pageId: params.pageId,
|
||||
spaceId: params.spaceId,
|
||||
assignedTo: params.assignedTo,
|
||||
status: params.status,
|
||||
limit: params.limit,
|
||||
cursor: params.cursor,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
tasks: data.tasks || [],
|
||||
nextCursor: data.nextCursor ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
tasks: {
|
||||
type: 'array',
|
||||
description: 'Array of Confluence tasks',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Task ID' },
|
||||
localId: { type: 'string', description: 'Local task ID', optional: true },
|
||||
spaceId: { type: 'string', description: 'Space ID', optional: true },
|
||||
pageId: { type: 'string', description: 'Page ID', optional: true },
|
||||
blogPostId: { type: 'string', description: 'Blog post ID', optional: true },
|
||||
status: { type: 'string', description: 'Task status (complete or incomplete)' },
|
||||
body: {
|
||||
type: 'string',
|
||||
description: 'Task body content in storage format',
|
||||
optional: true,
|
||||
},
|
||||
createdBy: { type: 'string', description: 'Creator account ID', optional: true },
|
||||
assignedTo: { type: 'string', description: 'Assignee account ID', optional: true },
|
||||
completedBy: { type: 'string', description: 'Completer account ID', optional: true },
|
||||
createdAt: { type: 'string', description: 'Creation timestamp', optional: true },
|
||||
updatedAt: { type: 'string', description: 'Last update timestamp', optional: true },
|
||||
dueAt: { type: 'string', description: 'Due date', optional: true },
|
||||
completedAt: { type: 'string', description: 'Completion timestamp', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
nextCursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for fetching the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
123
apps/sim/tools/confluence/update_blogpost.ts
Normal file
123
apps/sim/tools/confluence/update_blogpost.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import { TIMESTAMP_OUTPUT } from '@/tools/confluence/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export interface ConfluenceUpdateBlogPostParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
blogPostId: string
|
||||
title?: string
|
||||
content?: string
|
||||
cloudId?: string
|
||||
}
|
||||
|
||||
export interface ConfluenceUpdateBlogPostResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
ts: string
|
||||
blogPostId: string
|
||||
title: string
|
||||
status: string | null
|
||||
spaceId: string | null
|
||||
version: Record<string, unknown> | null
|
||||
url: string
|
||||
}
|
||||
}
|
||||
|
||||
export const confluenceUpdateBlogPostTool: ToolConfig<
|
||||
ConfluenceUpdateBlogPostParams,
|
||||
ConfluenceUpdateBlogPostResponse
|
||||
> = {
|
||||
id: 'confluence_update_blogpost',
|
||||
name: 'Confluence Update Blog Post',
|
||||
description: 'Update an existing Confluence blog post title and/or content.',
|
||||
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)',
|
||||
},
|
||||
blogPostId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the blog post to update',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New title for the blog post',
|
||||
},
|
||||
content: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New content for the blog post in storage format',
|
||||
},
|
||||
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/blogposts',
|
||||
method: 'PUT',
|
||||
headers: (params: ConfluenceUpdateBlogPostParams) => ({
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
body: (params: ConfluenceUpdateBlogPostParams) => ({
|
||||
domain: params.domain,
|
||||
accessToken: params.accessToken,
|
||||
cloudId: params.cloudId,
|
||||
blogPostId: params.blogPostId,
|
||||
title: params.title,
|
||||
content: params.content,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
blogPostId: data.id ?? '',
|
||||
title: data.title ?? '',
|
||||
status: data.status ?? null,
|
||||
spaceId: data.spaceId ?? null,
|
||||
version: data.version ?? null,
|
||||
url: data._links?.webui ?? '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
blogPostId: { type: 'string', description: 'Updated blog post ID' },
|
||||
title: { type: 'string', description: 'Blog post title' },
|
||||
status: { type: 'string', description: 'Blog post status', optional: true },
|
||||
spaceId: { type: 'string', description: 'Space ID', optional: true },
|
||||
version: { type: 'json', description: 'Version information', optional: true },
|
||||
url: { type: 'string', description: 'URL to view the blog post' },
|
||||
},
|
||||
}
|
||||
131
apps/sim/tools/confluence/update_space.ts
Normal file
131
apps/sim/tools/confluence/update_space.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
import { SPACE_DESCRIPTION_OUTPUT_PROPERTIES, TIMESTAMP_OUTPUT } from '@/tools/confluence/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export interface ConfluenceUpdateSpaceParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
spaceId: string
|
||||
name?: string
|
||||
description?: string
|
||||
cloudId?: string
|
||||
}
|
||||
|
||||
export interface ConfluenceUpdateSpaceResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
ts: string
|
||||
spaceId: string
|
||||
name: string
|
||||
key: string
|
||||
type: string
|
||||
status: string
|
||||
url: string
|
||||
description: { value: string; representation: string } | null
|
||||
}
|
||||
}
|
||||
|
||||
export const confluenceUpdateSpaceTool: ToolConfig<
|
||||
ConfluenceUpdateSpaceParams,
|
||||
ConfluenceUpdateSpaceResponse
|
||||
> = {
|
||||
id: 'confluence_update_space',
|
||||
name: 'Confluence Update Space',
|
||||
description: 'Update a Confluence space name or description.',
|
||||
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)',
|
||||
},
|
||||
spaceId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the space to update',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New name for the space',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New description for the space',
|
||||
},
|
||||
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/space',
|
||||
method: 'PUT',
|
||||
headers: (params: ConfluenceUpdateSpaceParams) => ({
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
body: (params: ConfluenceUpdateSpaceParams) => ({
|
||||
domain: params.domain,
|
||||
accessToken: params.accessToken,
|
||||
cloudId: params.cloudId,
|
||||
spaceId: params.spaceId,
|
||||
name: params.name,
|
||||
description: params.description,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
spaceId: data.id ?? '',
|
||||
name: data.name ?? '',
|
||||
key: data.key ?? '',
|
||||
type: data.type ?? '',
|
||||
status: data.status ?? '',
|
||||
url: data._links?.webui ?? '',
|
||||
description: data.description ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
spaceId: { type: 'string', description: 'Updated space ID' },
|
||||
name: { type: 'string', description: 'Space name' },
|
||||
key: { type: 'string', description: 'Space key' },
|
||||
type: { type: 'string', description: 'Space type' },
|
||||
status: { type: 'string', description: 'Space status' },
|
||||
url: { type: 'string', description: 'URL to view the space' },
|
||||
description: {
|
||||
type: 'object',
|
||||
description: 'Space description',
|
||||
properties: SPACE_DESCRIPTION_OUTPUT_PROPERTIES,
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
141
apps/sim/tools/confluence/update_task.ts
Normal file
141
apps/sim/tools/confluence/update_task.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
import { TIMESTAMP_OUTPUT } from '@/tools/confluence/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export interface ConfluenceUpdateTaskParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
taskId: string
|
||||
status: string
|
||||
cloudId?: string
|
||||
}
|
||||
|
||||
export interface ConfluenceUpdateTaskResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
ts: string
|
||||
id: string
|
||||
localId: string | null
|
||||
spaceId: string | null
|
||||
pageId: string | null
|
||||
blogPostId: string | null
|
||||
status: string
|
||||
body: string | null
|
||||
createdBy: string | null
|
||||
assignedTo: string | null
|
||||
completedBy: string | null
|
||||
createdAt: string | null
|
||||
updatedAt: string | null
|
||||
dueAt: string | null
|
||||
completedAt: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export const confluenceUpdateTaskTool: ToolConfig<
|
||||
ConfluenceUpdateTaskParams,
|
||||
ConfluenceUpdateTaskResponse
|
||||
> = {
|
||||
id: 'confluence_update_task',
|
||||
name: 'Confluence Update Task',
|
||||
description: 'Update the status of a Confluence inline task (complete or incomplete).',
|
||||
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)',
|
||||
},
|
||||
taskId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the task to update',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New status for the task (complete or incomplete)',
|
||||
},
|
||||
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/tasks',
|
||||
method: 'POST',
|
||||
headers: (params: ConfluenceUpdateTaskParams) => ({
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
body: (params: ConfluenceUpdateTaskParams) => ({
|
||||
domain: params.domain,
|
||||
accessToken: params.accessToken,
|
||||
cloudId: params.cloudId,
|
||||
action: 'update',
|
||||
taskId: params.taskId,
|
||||
status: params.status,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
const task = data.task || data
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
id: task.id ?? '',
|
||||
localId: task.localId ?? null,
|
||||
spaceId: task.spaceId ?? null,
|
||||
pageId: task.pageId ?? null,
|
||||
blogPostId: task.blogPostId ?? null,
|
||||
status: task.status ?? '',
|
||||
body: task.body ?? null,
|
||||
createdBy: task.createdBy ?? null,
|
||||
assignedTo: task.assignedTo ?? null,
|
||||
completedBy: task.completedBy ?? null,
|
||||
createdAt: task.createdAt ?? null,
|
||||
updatedAt: task.updatedAt ?? null,
|
||||
dueAt: task.dueAt ?? null,
|
||||
completedAt: task.completedAt ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
id: { type: 'string', description: 'Task ID' },
|
||||
localId: { type: 'string', description: 'Local task ID', optional: true },
|
||||
spaceId: { type: 'string', description: 'Space ID', optional: true },
|
||||
pageId: { type: 'string', description: 'Page ID', optional: true },
|
||||
blogPostId: { type: 'string', description: 'Blog post ID', optional: true },
|
||||
status: { type: 'string', description: 'Updated task status' },
|
||||
body: { type: 'string', description: 'Task body content in storage format', optional: true },
|
||||
createdBy: { type: 'string', description: 'Creator account ID', optional: true },
|
||||
assignedTo: { type: 'string', description: 'Assignee account ID', optional: true },
|
||||
completedBy: { type: 'string', description: 'Completer account ID', optional: true },
|
||||
createdAt: { type: 'string', description: 'Creation timestamp', optional: true },
|
||||
updatedAt: { type: 'string', description: 'Last update timestamp', optional: true },
|
||||
dueAt: { type: 'string', description: 'Due date', optional: true },
|
||||
completedAt: { type: 'string', description: 'Completion timestamp', optional: true },
|
||||
},
|
||||
}
|
||||
105
apps/sim/tools/devin/create_session.ts
Normal file
105
apps/sim/tools/devin/create_session.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { DevinCreateSessionParams, DevinCreateSessionResponse } from './types'
|
||||
import { DEVIN_SESSION_OUTPUT_PROPERTIES } from './types'
|
||||
|
||||
export const devinCreateSessionTool: ToolConfig<
|
||||
DevinCreateSessionParams,
|
||||
DevinCreateSessionResponse
|
||||
> = {
|
||||
id: 'devin_create_session',
|
||||
name: 'create_session',
|
||||
description:
|
||||
'Create a new Devin session with a prompt. Devin will autonomously work on the task described in the prompt.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Devin API key (service user credential starting with cog_)',
|
||||
},
|
||||
prompt: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The task prompt for Devin to work on',
|
||||
},
|
||||
playbookId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Optional playbook ID to guide the session',
|
||||
},
|
||||
maxAcuLimit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum ACU limit for the session',
|
||||
},
|
||||
tags: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated tags for the session',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.devin.ai/v3/organizations/sessions',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {
|
||||
prompt: params.prompt,
|
||||
}
|
||||
if (params.playbookId) body.playbook_id = params.playbookId
|
||||
if (params.maxAcuLimit != null) {
|
||||
body.max_acu_limit = params.maxAcuLimit
|
||||
}
|
||||
if (params.tags) {
|
||||
body.tags = params.tags.split(',').map((t: string) => t.trim())
|
||||
}
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
sessionId: data.session_id ?? null,
|
||||
url: data.url ?? null,
|
||||
status: data.status ?? null,
|
||||
statusDetail: data.status_detail ?? null,
|
||||
title: data.title ?? null,
|
||||
createdAt: data.created_at ?? null,
|
||||
updatedAt: data.updated_at ?? null,
|
||||
acusConsumed: data.acus_consumed ?? null,
|
||||
tags: data.tags ?? null,
|
||||
pullRequests: data.pull_requests ?? null,
|
||||
structuredOutput: data.structured_output ?? null,
|
||||
playbookId: data.playbook_id ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
sessionId: DEVIN_SESSION_OUTPUT_PROPERTIES.sessionId,
|
||||
url: DEVIN_SESSION_OUTPUT_PROPERTIES.url,
|
||||
status: DEVIN_SESSION_OUTPUT_PROPERTIES.status,
|
||||
statusDetail: DEVIN_SESSION_OUTPUT_PROPERTIES.statusDetail,
|
||||
title: DEVIN_SESSION_OUTPUT_PROPERTIES.title,
|
||||
createdAt: DEVIN_SESSION_OUTPUT_PROPERTIES.createdAt,
|
||||
updatedAt: DEVIN_SESSION_OUTPUT_PROPERTIES.updatedAt,
|
||||
acusConsumed: DEVIN_SESSION_OUTPUT_PROPERTIES.acusConsumed,
|
||||
tags: DEVIN_SESSION_OUTPUT_PROPERTIES.tags,
|
||||
pullRequests: DEVIN_SESSION_OUTPUT_PROPERTIES.pullRequests,
|
||||
structuredOutput: DEVIN_SESSION_OUTPUT_PROPERTIES.structuredOutput,
|
||||
playbookId: DEVIN_SESSION_OUTPUT_PROPERTIES.playbookId,
|
||||
},
|
||||
}
|
||||
70
apps/sim/tools/devin/get_session.ts
Normal file
70
apps/sim/tools/devin/get_session.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { DevinGetSessionParams, DevinGetSessionResponse } from './types'
|
||||
import { DEVIN_SESSION_OUTPUT_PROPERTIES } from './types'
|
||||
|
||||
export const devinGetSessionTool: ToolConfig<DevinGetSessionParams, DevinGetSessionResponse> = {
|
||||
id: 'devin_get_session',
|
||||
name: 'get_session',
|
||||
description:
|
||||
'Retrieve details of an existing Devin session including status, tags, pull requests, and structured output.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Devin API key (service user credential starting with cog_)',
|
||||
},
|
||||
sessionId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The session ID to retrieve',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.devin.ai/v3/organizations/sessions/${params.sessionId}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
sessionId: data.session_id ?? null,
|
||||
url: data.url ?? null,
|
||||
status: data.status ?? null,
|
||||
statusDetail: data.status_detail ?? null,
|
||||
title: data.title ?? null,
|
||||
createdAt: data.created_at ?? null,
|
||||
updatedAt: data.updated_at ?? null,
|
||||
acusConsumed: data.acus_consumed ?? null,
|
||||
tags: data.tags ?? null,
|
||||
pullRequests: data.pull_requests ?? null,
|
||||
structuredOutput: data.structured_output ?? null,
|
||||
playbookId: data.playbook_id ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
sessionId: DEVIN_SESSION_OUTPUT_PROPERTIES.sessionId,
|
||||
url: DEVIN_SESSION_OUTPUT_PROPERTIES.url,
|
||||
status: DEVIN_SESSION_OUTPUT_PROPERTIES.status,
|
||||
statusDetail: DEVIN_SESSION_OUTPUT_PROPERTIES.statusDetail,
|
||||
title: DEVIN_SESSION_OUTPUT_PROPERTIES.title,
|
||||
createdAt: DEVIN_SESSION_OUTPUT_PROPERTIES.createdAt,
|
||||
updatedAt: DEVIN_SESSION_OUTPUT_PROPERTIES.updatedAt,
|
||||
acusConsumed: DEVIN_SESSION_OUTPUT_PROPERTIES.acusConsumed,
|
||||
tags: DEVIN_SESSION_OUTPUT_PROPERTIES.tags,
|
||||
pullRequests: DEVIN_SESSION_OUTPUT_PROPERTIES.pullRequests,
|
||||
structuredOutput: DEVIN_SESSION_OUTPUT_PROPERTIES.structuredOutput,
|
||||
playbookId: DEVIN_SESSION_OUTPUT_PROPERTIES.playbookId,
|
||||
},
|
||||
}
|
||||
4
apps/sim/tools/devin/index.ts
Normal file
4
apps/sim/tools/devin/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export { devinCreateSessionTool } from './create_session'
|
||||
export { devinGetSessionTool } from './get_session'
|
||||
export { devinListSessionsTool } from './list_sessions'
|
||||
export { devinSendMessageTool } from './send_message'
|
||||
70
apps/sim/tools/devin/list_sessions.ts
Normal file
70
apps/sim/tools/devin/list_sessions.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { DevinListSessionsParams, DevinListSessionsResponse } from './types'
|
||||
import { DEVIN_SESSION_LIST_ITEM_PROPERTIES } from './types'
|
||||
|
||||
export const devinListSessionsTool: ToolConfig<DevinListSessionsParams, DevinListSessionsResponse> =
|
||||
{
|
||||
id: 'devin_list_sessions',
|
||||
name: 'list_sessions',
|
||||
description: 'List Devin sessions in the organization. Returns up to 100 sessions by default.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Devin API key (service user credential starting with cog_)',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of sessions to return (1-200, default: 100)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const searchParams = new URLSearchParams()
|
||||
if (params.limit) searchParams.set('first', String(params.limit))
|
||||
const qs = searchParams.toString()
|
||||
return `https://api.devin.ai/v3/organizations/sessions${qs ? `?${qs}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
const items = data.items ?? []
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
sessions: items.map((item: Record<string, unknown>) => ({
|
||||
sessionId: item.session_id ?? null,
|
||||
url: item.url ?? null,
|
||||
status: item.status ?? null,
|
||||
statusDetail: item.status_detail ?? null,
|
||||
title: item.title ?? null,
|
||||
createdAt: item.created_at ?? null,
|
||||
updatedAt: item.updated_at ?? null,
|
||||
tags: item.tags ?? null,
|
||||
})),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
sessions: {
|
||||
type: 'array',
|
||||
description: 'List of Devin sessions',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: DEVIN_SESSION_LIST_ITEM_PROPERTIES,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
80
apps/sim/tools/devin/send_message.ts
Normal file
80
apps/sim/tools/devin/send_message.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { DevinSendMessageParams, DevinSendMessageResponse } from './types'
|
||||
import { DEVIN_SESSION_OUTPUT_PROPERTIES } from './types'
|
||||
|
||||
export const devinSendMessageTool: ToolConfig<DevinSendMessageParams, DevinSendMessageResponse> = {
|
||||
id: 'devin_send_message',
|
||||
name: 'send_message',
|
||||
description:
|
||||
'Send a message to a Devin session. If the session is suspended, it will be automatically resumed. Returns the updated session state.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Devin API key (service user credential starting with cog_)',
|
||||
},
|
||||
sessionId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The session ID to send the message to',
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The message to send to Devin',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.devin.ai/v3/organizations/sessions/${params.sessionId}/messages`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => ({
|
||||
message: params.message,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
sessionId: data.session_id ?? null,
|
||||
url: data.url ?? null,
|
||||
status: data.status ?? null,
|
||||
statusDetail: data.status_detail ?? null,
|
||||
title: data.title ?? null,
|
||||
createdAt: data.created_at ?? null,
|
||||
updatedAt: data.updated_at ?? null,
|
||||
acusConsumed: data.acus_consumed ?? null,
|
||||
tags: data.tags ?? null,
|
||||
pullRequests: data.pull_requests ?? null,
|
||||
structuredOutput: data.structured_output ?? null,
|
||||
playbookId: data.playbook_id ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
sessionId: DEVIN_SESSION_OUTPUT_PROPERTIES.sessionId,
|
||||
url: DEVIN_SESSION_OUTPUT_PROPERTIES.url,
|
||||
status: DEVIN_SESSION_OUTPUT_PROPERTIES.status,
|
||||
statusDetail: DEVIN_SESSION_OUTPUT_PROPERTIES.statusDetail,
|
||||
title: DEVIN_SESSION_OUTPUT_PROPERTIES.title,
|
||||
createdAt: DEVIN_SESSION_OUTPUT_PROPERTIES.createdAt,
|
||||
updatedAt: DEVIN_SESSION_OUTPUT_PROPERTIES.updatedAt,
|
||||
acusConsumed: DEVIN_SESSION_OUTPUT_PROPERTIES.acusConsumed,
|
||||
tags: DEVIN_SESSION_OUTPUT_PROPERTIES.tags,
|
||||
pullRequests: DEVIN_SESSION_OUTPUT_PROPERTIES.pullRequests,
|
||||
structuredOutput: DEVIN_SESSION_OUTPUT_PROPERTIES.structuredOutput,
|
||||
playbookId: DEVIN_SESSION_OUTPUT_PROPERTIES.playbookId,
|
||||
},
|
||||
}
|
||||
168
apps/sim/tools/devin/types.ts
Normal file
168
apps/sim/tools/devin/types.ts
Normal file
@@ -0,0 +1,168 @@
|
||||
import type { OutputProperty, ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface DevinCreateSessionParams {
|
||||
apiKey: string
|
||||
prompt: string
|
||||
playbookId?: string
|
||||
maxAcuLimit?: number
|
||||
tags?: string
|
||||
}
|
||||
|
||||
export interface DevinGetSessionParams {
|
||||
apiKey: string
|
||||
sessionId: string
|
||||
}
|
||||
|
||||
export interface DevinListSessionsParams {
|
||||
apiKey: string
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface DevinSendMessageParams {
|
||||
apiKey: string
|
||||
sessionId: string
|
||||
message: string
|
||||
}
|
||||
|
||||
export const DEVIN_SESSION_OUTPUT_PROPERTIES = {
|
||||
sessionId: {
|
||||
type: 'string',
|
||||
description: 'Unique identifier for the session',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'URL to view the session in the Devin UI',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Session status (new, claimed, running, exit, error, suspended, resuming)',
|
||||
},
|
||||
statusDetail: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Detailed status (working, waiting_for_user, waiting_for_approval, finished, inactivity, etc.)',
|
||||
optional: true,
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
description: 'Session title',
|
||||
optional: true,
|
||||
},
|
||||
createdAt: {
|
||||
type: 'number',
|
||||
description: 'Unix timestamp when the session was created',
|
||||
optional: true,
|
||||
},
|
||||
updatedAt: {
|
||||
type: 'number',
|
||||
description: 'Unix timestamp when the session was last updated',
|
||||
optional: true,
|
||||
},
|
||||
acusConsumed: {
|
||||
type: 'number',
|
||||
description: 'ACUs consumed by the session',
|
||||
optional: true,
|
||||
},
|
||||
tags: {
|
||||
type: 'json',
|
||||
description: 'Tags associated with the session',
|
||||
optional: true,
|
||||
},
|
||||
pullRequests: {
|
||||
type: 'json',
|
||||
description: 'Pull requests created during the session',
|
||||
optional: true,
|
||||
},
|
||||
structuredOutput: {
|
||||
type: 'json',
|
||||
description: 'Structured output from the session',
|
||||
optional: true,
|
||||
},
|
||||
playbookId: {
|
||||
type: 'string',
|
||||
description: 'Associated playbook ID',
|
||||
optional: true,
|
||||
},
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
|
||||
export const DEVIN_SESSION_LIST_ITEM_PROPERTIES = {
|
||||
sessionId: {
|
||||
type: 'string',
|
||||
description: 'Unique identifier for the session',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'URL to view the session',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Session status',
|
||||
},
|
||||
statusDetail: {
|
||||
type: 'string',
|
||||
description: 'Detailed status',
|
||||
optional: true,
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
description: 'Session title',
|
||||
optional: true,
|
||||
},
|
||||
createdAt: {
|
||||
type: 'number',
|
||||
description: 'Creation timestamp (Unix)',
|
||||
optional: true,
|
||||
},
|
||||
updatedAt: {
|
||||
type: 'number',
|
||||
description: 'Last updated timestamp (Unix)',
|
||||
optional: true,
|
||||
},
|
||||
tags: {
|
||||
type: 'json',
|
||||
description: 'Session tags',
|
||||
optional: true,
|
||||
},
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
|
||||
export interface DevinSessionOutput {
|
||||
sessionId: string
|
||||
url: string
|
||||
status: string
|
||||
statusDetail: string | null
|
||||
title: string | null
|
||||
createdAt: number | null
|
||||
updatedAt: number | null
|
||||
acusConsumed: number | null
|
||||
tags: string[] | null
|
||||
pullRequests: Array<{ pr_url: string; pr_state: string | null }> | null
|
||||
structuredOutput: Record<string, unknown> | null
|
||||
playbookId: string | null
|
||||
}
|
||||
|
||||
export interface DevinCreateSessionResponse extends ToolResponse {
|
||||
output: DevinSessionOutput
|
||||
}
|
||||
|
||||
export interface DevinGetSessionResponse extends ToolResponse {
|
||||
output: DevinSessionOutput
|
||||
}
|
||||
|
||||
export interface DevinListSessionsResponse extends ToolResponse {
|
||||
output: {
|
||||
sessions: Array<{
|
||||
sessionId: string
|
||||
url: string
|
||||
status: string
|
||||
statusDetail: string | null
|
||||
title: string | null
|
||||
createdAt: number | null
|
||||
updatedAt: number | null
|
||||
tags: string[] | null
|
||||
}>
|
||||
}
|
||||
}
|
||||
|
||||
export interface DevinSendMessageResponse extends ToolResponse {
|
||||
output: DevinSessionOutput
|
||||
}
|
||||
132
apps/sim/tools/google_bigquery/get_table.ts
Normal file
132
apps/sim/tools/google_bigquery/get_table.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
import type {
|
||||
GoogleBigQueryGetTableParams,
|
||||
GoogleBigQueryGetTableResponse,
|
||||
} from '@/tools/google_bigquery/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const googleBigQueryGetTableTool: ToolConfig<
|
||||
GoogleBigQueryGetTableParams,
|
||||
GoogleBigQueryGetTableResponse
|
||||
> = {
|
||||
id: 'google_bigquery_get_table',
|
||||
name: 'BigQuery Get Table',
|
||||
description: 'Get metadata and schema for a Google BigQuery table',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-bigquery',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
projectId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Google Cloud project ID',
|
||||
},
|
||||
datasetId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'BigQuery dataset ID',
|
||||
},
|
||||
tableId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'BigQuery table ID',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://bigquery.googleapis.com/bigquery/v2/projects/${encodeURIComponent(params.projectId)}/datasets/${encodeURIComponent(params.datasetId)}/tables/${encodeURIComponent(params.tableId)}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
if (!response.ok) {
|
||||
const errorMessage = data.error?.message || 'Failed to get BigQuery table'
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
|
||||
const schema = (data.schema?.fields ?? []).map(
|
||||
(f: { name: string; type: string; mode?: string; description?: string }) => ({
|
||||
name: f.name,
|
||||
type: f.type,
|
||||
mode: f.mode ?? null,
|
||||
description: f.description ?? null,
|
||||
})
|
||||
)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
tableId: data.tableReference?.tableId ?? null,
|
||||
datasetId: data.tableReference?.datasetId ?? null,
|
||||
projectId: data.tableReference?.projectId ?? null,
|
||||
type: data.type ?? null,
|
||||
description: data.description ?? null,
|
||||
numRows: data.numRows ?? null,
|
||||
numBytes: data.numBytes ?? null,
|
||||
schema,
|
||||
creationTime: data.creationTime ?? null,
|
||||
lastModifiedTime: data.lastModifiedTime ?? null,
|
||||
location: data.location ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
tableId: { type: 'string', description: 'Table ID' },
|
||||
datasetId: { type: 'string', description: 'Dataset ID' },
|
||||
projectId: { type: 'string', description: 'Project ID' },
|
||||
type: {
|
||||
type: 'string',
|
||||
description: 'Table type (TABLE, VIEW, SNAPSHOT, MATERIALIZED_VIEW, EXTERNAL)',
|
||||
},
|
||||
description: { type: 'string', description: 'Table description', optional: true },
|
||||
numRows: { type: 'string', description: 'Total number of rows' },
|
||||
numBytes: {
|
||||
type: 'string',
|
||||
description: 'Total size in bytes, excluding data in streaming buffer',
|
||||
},
|
||||
schema: {
|
||||
type: 'array',
|
||||
description: 'Array of column definitions',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string', description: 'Column name' },
|
||||
type: {
|
||||
type: 'string',
|
||||
description: 'Data type (STRING, INTEGER, FLOAT, BOOLEAN, TIMESTAMP, RECORD, etc.)',
|
||||
},
|
||||
mode: {
|
||||
type: 'string',
|
||||
description: 'Column mode (NULLABLE, REQUIRED, or REPEATED)',
|
||||
optional: true,
|
||||
},
|
||||
description: { type: 'string', description: 'Column description', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
creationTime: { type: 'string', description: 'Table creation time (milliseconds since epoch)' },
|
||||
lastModifiedTime: {
|
||||
type: 'string',
|
||||
description: 'Last modification time (milliseconds since epoch)',
|
||||
},
|
||||
location: { type: 'string', description: 'Geographic location where the table resides' },
|
||||
},
|
||||
}
|
||||
5
apps/sim/tools/google_bigquery/index.ts
Normal file
5
apps/sim/tools/google_bigquery/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export { googleBigQueryGetTableTool } from '@/tools/google_bigquery/get_table'
|
||||
export { googleBigQueryInsertRowsTool } from '@/tools/google_bigquery/insert_rows'
|
||||
export { googleBigQueryListDatasetsTool } from '@/tools/google_bigquery/list_datasets'
|
||||
export { googleBigQueryListTablesTool } from '@/tools/google_bigquery/list_tables'
|
||||
export { googleBigQueryQueryTool } from '@/tools/google_bigquery/query'
|
||||
174
apps/sim/tools/google_bigquery/insert_rows.ts
Normal file
174
apps/sim/tools/google_bigquery/insert_rows.ts
Normal file
@@ -0,0 +1,174 @@
|
||||
import type {
|
||||
GoogleBigQueryInsertRowsParams,
|
||||
GoogleBigQueryInsertRowsResponse,
|
||||
} from '@/tools/google_bigquery/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const googleBigQueryInsertRowsTool: ToolConfig<
|
||||
GoogleBigQueryInsertRowsParams,
|
||||
GoogleBigQueryInsertRowsResponse
|
||||
> = {
|
||||
id: 'google_bigquery_insert_rows',
|
||||
name: 'BigQuery Insert Rows',
|
||||
description: 'Insert rows into a Google BigQuery table using streaming insert',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-bigquery',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
projectId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Google Cloud project ID',
|
||||
},
|
||||
datasetId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'BigQuery dataset ID',
|
||||
},
|
||||
tableId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'BigQuery table ID',
|
||||
},
|
||||
rows: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'JSON array of row objects to insert',
|
||||
},
|
||||
skipInvalidRows: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to insert valid rows even if some are invalid',
|
||||
},
|
||||
ignoreUnknownValues: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to ignore columns not in the table schema',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://bigquery.googleapis.com/bigquery/v2/projects/${encodeURIComponent(params.projectId)}/datasets/${encodeURIComponent(params.datasetId)}/tables/${encodeURIComponent(params.tableId)}/insertAll`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const parsedRows = typeof params.rows === 'string' ? JSON.parse(params.rows) : params.rows
|
||||
const rows = (parsedRows as Record<string, unknown>[]).map(
|
||||
(row: Record<string, unknown>) => ({ json: row })
|
||||
)
|
||||
|
||||
const body: Record<string, unknown> = { rows }
|
||||
if (params.skipInvalidRows !== undefined) body.skipInvalidRows = params.skipInvalidRows
|
||||
if (params.ignoreUnknownValues !== undefined)
|
||||
body.ignoreUnknownValues = params.ignoreUnknownValues
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: GoogleBigQueryInsertRowsParams) => {
|
||||
const data = await response.json()
|
||||
if (!response.ok) {
|
||||
const errorMessage = data.error?.message || 'Failed to insert rows into BigQuery table'
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
|
||||
const insertErrors = data.insertErrors ?? []
|
||||
const errors = insertErrors.map(
|
||||
(err: {
|
||||
index: number
|
||||
errors: Array<{ reason?: string; location?: string; message?: string }>
|
||||
}) => ({
|
||||
index: err.index,
|
||||
errors: err.errors.map((e) => ({
|
||||
reason: e.reason ?? null,
|
||||
location: e.location ?? null,
|
||||
message: e.message ?? null,
|
||||
})),
|
||||
})
|
||||
)
|
||||
|
||||
let totalRows = 0
|
||||
if (params?.rows) {
|
||||
const parsed = typeof params.rows === 'string' ? JSON.parse(params.rows) : params.rows
|
||||
totalRows = Array.isArray(parsed) ? parsed.length : 0
|
||||
}
|
||||
|
||||
// When insertErrors is empty, all rows succeeded.
|
||||
// When insertErrors is present and skipInvalidRows is false (default),
|
||||
// the entire batch is rejected — no rows are inserted.
|
||||
let insertedRows = 0
|
||||
if (insertErrors.length === 0) {
|
||||
insertedRows = totalRows
|
||||
} else if (params?.skipInvalidRows) {
|
||||
const failedIndexes = new Set(insertErrors.map((e: { index: number }) => e.index))
|
||||
insertedRows = totalRows - failedIndexes.size
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
insertedRows,
|
||||
errors,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
insertedRows: { type: 'number', description: 'Number of rows successfully inserted' },
|
||||
errors: {
|
||||
type: 'array',
|
||||
description: 'Array of per-row insertion errors (empty if all succeeded)',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
index: { type: 'number', description: 'Zero-based index of the row that failed' },
|
||||
errors: {
|
||||
type: 'array',
|
||||
description: 'Error details for this row',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
reason: {
|
||||
type: 'string',
|
||||
description: 'Short error code summarizing the error',
|
||||
optional: true,
|
||||
},
|
||||
location: {
|
||||
type: 'string',
|
||||
description: 'Where the error occurred',
|
||||
optional: true,
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
description: 'Human-readable error description',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
121
apps/sim/tools/google_bigquery/list_datasets.ts
Normal file
121
apps/sim/tools/google_bigquery/list_datasets.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import type {
|
||||
GoogleBigQueryListDatasetsParams,
|
||||
GoogleBigQueryListDatasetsResponse,
|
||||
} from '@/tools/google_bigquery/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const googleBigQueryListDatasetsTool: ToolConfig<
|
||||
GoogleBigQueryListDatasetsParams,
|
||||
GoogleBigQueryListDatasetsResponse
|
||||
> = {
|
||||
id: 'google_bigquery_list_datasets',
|
||||
name: 'BigQuery List Datasets',
|
||||
description: 'List all datasets in a Google BigQuery project',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-bigquery',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
projectId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Google Cloud project ID',
|
||||
},
|
||||
maxResults: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of datasets to return',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Token for pagination',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(
|
||||
`https://bigquery.googleapis.com/bigquery/v2/projects/${encodeURIComponent(params.projectId)}/datasets`
|
||||
)
|
||||
if (params.maxResults !== undefined && params.maxResults !== null) {
|
||||
const maxResults = Number(params.maxResults)
|
||||
if (Number.isFinite(maxResults) && maxResults > 0) {
|
||||
url.searchParams.set('maxResults', String(maxResults))
|
||||
}
|
||||
}
|
||||
if (params.pageToken) url.searchParams.set('pageToken', params.pageToken)
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
if (!response.ok) {
|
||||
const errorMessage = data.error?.message || 'Failed to list BigQuery datasets'
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
|
||||
const datasets = (data.datasets ?? []).map(
|
||||
(ds: {
|
||||
datasetReference: { datasetId: string; projectId: string }
|
||||
friendlyName?: string
|
||||
location?: string
|
||||
}) => ({
|
||||
datasetId: ds.datasetReference.datasetId,
|
||||
projectId: ds.datasetReference.projectId,
|
||||
friendlyName: ds.friendlyName ?? null,
|
||||
location: ds.location ?? null,
|
||||
})
|
||||
)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
datasets,
|
||||
nextPageToken: data.nextPageToken ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
datasets: {
|
||||
type: 'array',
|
||||
description: 'Array of dataset objects',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
datasetId: { type: 'string', description: 'Unique dataset identifier' },
|
||||
projectId: { type: 'string', description: 'Project ID containing this dataset' },
|
||||
friendlyName: {
|
||||
type: 'string',
|
||||
description: 'Descriptive name for the dataset',
|
||||
optional: true,
|
||||
},
|
||||
location: { type: 'string', description: 'Geographic location where the data resides' },
|
||||
},
|
||||
},
|
||||
},
|
||||
nextPageToken: {
|
||||
type: 'string',
|
||||
description: 'Token for fetching next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
142
apps/sim/tools/google_bigquery/list_tables.ts
Normal file
142
apps/sim/tools/google_bigquery/list_tables.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
import type {
|
||||
GoogleBigQueryListTablesParams,
|
||||
GoogleBigQueryListTablesResponse,
|
||||
} from '@/tools/google_bigquery/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const googleBigQueryListTablesTool: ToolConfig<
|
||||
GoogleBigQueryListTablesParams,
|
||||
GoogleBigQueryListTablesResponse
|
||||
> = {
|
||||
id: 'google_bigquery_list_tables',
|
||||
name: 'BigQuery List Tables',
|
||||
description: 'List all tables in a Google BigQuery dataset',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-bigquery',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
projectId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Google Cloud project ID',
|
||||
},
|
||||
datasetId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'BigQuery dataset ID',
|
||||
},
|
||||
maxResults: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of tables to return',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Token for pagination',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(
|
||||
`https://bigquery.googleapis.com/bigquery/v2/projects/${encodeURIComponent(params.projectId)}/datasets/${encodeURIComponent(params.datasetId)}/tables`
|
||||
)
|
||||
if (params.maxResults !== undefined && params.maxResults !== null) {
|
||||
const maxResults = Number(params.maxResults)
|
||||
if (Number.isFinite(maxResults) && maxResults > 0) {
|
||||
url.searchParams.set('maxResults', String(maxResults))
|
||||
}
|
||||
}
|
||||
if (params.pageToken) url.searchParams.set('pageToken', params.pageToken)
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
if (!response.ok) {
|
||||
const errorMessage = data.error?.message || 'Failed to list BigQuery tables'
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
|
||||
const tables = (data.tables ?? []).map(
|
||||
(t: {
|
||||
tableReference: { tableId: string; datasetId: string; projectId: string }
|
||||
type?: string
|
||||
friendlyName?: string
|
||||
creationTime?: string
|
||||
}) => ({
|
||||
tableId: t.tableReference.tableId,
|
||||
datasetId: t.tableReference.datasetId,
|
||||
projectId: t.tableReference.projectId,
|
||||
type: t.type ?? null,
|
||||
friendlyName: t.friendlyName ?? null,
|
||||
creationTime: t.creationTime ?? null,
|
||||
})
|
||||
)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
tables,
|
||||
totalItems: data.totalItems ?? null,
|
||||
nextPageToken: data.nextPageToken ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
tables: {
|
||||
type: 'array',
|
||||
description: 'Array of table objects',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
tableId: { type: 'string', description: 'Table identifier' },
|
||||
datasetId: { type: 'string', description: 'Dataset ID containing this table' },
|
||||
projectId: { type: 'string', description: 'Project ID containing this table' },
|
||||
type: { type: 'string', description: 'Table type (TABLE, VIEW, EXTERNAL, etc.)' },
|
||||
friendlyName: {
|
||||
type: 'string',
|
||||
description: 'User-friendly name for the table',
|
||||
optional: true,
|
||||
},
|
||||
creationTime: {
|
||||
type: 'string',
|
||||
description: 'Time when created, in milliseconds since epoch',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
totalItems: {
|
||||
type: 'number',
|
||||
description: 'Total number of tables in the dataset',
|
||||
optional: true,
|
||||
},
|
||||
nextPageToken: {
|
||||
type: 'string',
|
||||
description: 'Token for fetching next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
164
apps/sim/tools/google_bigquery/query.ts
Normal file
164
apps/sim/tools/google_bigquery/query.ts
Normal file
@@ -0,0 +1,164 @@
|
||||
import type {
|
||||
GoogleBigQueryQueryParams,
|
||||
GoogleBigQueryQueryResponse,
|
||||
} from '@/tools/google_bigquery/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const googleBigQueryQueryTool: ToolConfig<
|
||||
GoogleBigQueryQueryParams,
|
||||
GoogleBigQueryQueryResponse
|
||||
> = {
|
||||
id: 'google_bigquery_query',
|
||||
name: 'BigQuery Run Query',
|
||||
description: 'Run a SQL query against Google BigQuery and return the results',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-bigquery',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
projectId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Google Cloud project ID',
|
||||
},
|
||||
query: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'SQL query to execute',
|
||||
},
|
||||
useLegacySql: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to use legacy SQL syntax (default: false)',
|
||||
},
|
||||
maxResults: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of rows to return',
|
||||
},
|
||||
defaultDatasetId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Default dataset for unqualified table names',
|
||||
},
|
||||
location: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Processing location (e.g., "US", "EU")',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://bigquery.googleapis.com/bigquery/v2/projects/${encodeURIComponent(params.projectId)}/queries`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {
|
||||
query: params.query,
|
||||
useLegacySql: params.useLegacySql ?? false,
|
||||
}
|
||||
if (params.maxResults !== undefined) body.maxResults = Number(params.maxResults)
|
||||
if (params.defaultDatasetId) {
|
||||
body.defaultDataset = {
|
||||
projectId: params.projectId,
|
||||
datasetId: params.defaultDatasetId,
|
||||
}
|
||||
}
|
||||
if (params.location) body.location = params.location
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
if (!response.ok) {
|
||||
const errorMessage = data.error?.message || 'Failed to execute BigQuery query'
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
|
||||
const columns = (data.schema?.fields ?? []).map((f: { name: string }) => f.name)
|
||||
const rows = (data.rows ?? []).map((row: { f: Array<{ v: unknown }> }) => {
|
||||
const obj: Record<string, unknown> = {}
|
||||
row.f.forEach((field, index) => {
|
||||
obj[columns[index]] = field.v ?? null
|
||||
})
|
||||
return obj
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
columns,
|
||||
rows,
|
||||
totalRows: data.totalRows ?? null,
|
||||
jobComplete: data.jobComplete ?? false,
|
||||
totalBytesProcessed: data.totalBytesProcessed ?? null,
|
||||
cacheHit: data.cacheHit ?? null,
|
||||
jobReference: data.jobReference ?? null,
|
||||
pageToken: data.pageToken ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
columns: {
|
||||
type: 'array',
|
||||
description: 'Array of column names from the query result',
|
||||
items: { type: 'string', description: 'Column name' },
|
||||
},
|
||||
rows: {
|
||||
type: 'array',
|
||||
description: 'Array of row objects keyed by column name',
|
||||
items: {
|
||||
type: 'object',
|
||||
description: 'Row with column name/value pairs',
|
||||
},
|
||||
},
|
||||
totalRows: {
|
||||
type: 'string',
|
||||
description: 'Total number of rows in the complete result set',
|
||||
optional: true,
|
||||
},
|
||||
jobComplete: { type: 'boolean', description: 'Whether the query completed within the timeout' },
|
||||
totalBytesProcessed: { type: 'string', description: 'Total bytes processed by the query' },
|
||||
cacheHit: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the query result was served from cache',
|
||||
optional: true,
|
||||
},
|
||||
jobReference: {
|
||||
type: 'object',
|
||||
description: 'Job reference (useful when jobComplete is false)',
|
||||
optional: true,
|
||||
properties: {
|
||||
projectId: { type: 'string', description: 'Project ID containing the job' },
|
||||
jobId: { type: 'string', description: 'Unique job identifier' },
|
||||
location: { type: 'string', description: 'Geographic location of the job' },
|
||||
},
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
description: 'Token for fetching additional result pages',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
119
apps/sim/tools/google_bigquery/types.ts
Normal file
119
apps/sim/tools/google_bigquery/types.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface GoogleBigQueryBaseParams {
|
||||
accessToken: string
|
||||
projectId: string
|
||||
}
|
||||
|
||||
export interface GoogleBigQueryQueryParams extends GoogleBigQueryBaseParams {
|
||||
query: string
|
||||
useLegacySql?: boolean
|
||||
maxResults?: number
|
||||
defaultDatasetId?: string
|
||||
location?: string
|
||||
}
|
||||
|
||||
export interface GoogleBigQueryListDatasetsParams extends GoogleBigQueryBaseParams {
|
||||
maxResults?: number
|
||||
pageToken?: string
|
||||
}
|
||||
|
||||
export interface GoogleBigQueryListTablesParams extends GoogleBigQueryBaseParams {
|
||||
datasetId: string
|
||||
maxResults?: number
|
||||
pageToken?: string
|
||||
}
|
||||
|
||||
export interface GoogleBigQueryGetTableParams extends GoogleBigQueryBaseParams {
|
||||
datasetId: string
|
||||
tableId: string
|
||||
}
|
||||
|
||||
export interface GoogleBigQueryInsertRowsParams extends GoogleBigQueryBaseParams {
|
||||
datasetId: string
|
||||
tableId: string
|
||||
rows: string
|
||||
skipInvalidRows?: boolean
|
||||
ignoreUnknownValues?: boolean
|
||||
}
|
||||
|
||||
export interface GoogleBigQueryJobReference {
|
||||
projectId: string
|
||||
jobId: string
|
||||
location: string
|
||||
}
|
||||
|
||||
export interface GoogleBigQueryQueryResponse extends ToolResponse {
|
||||
output: {
|
||||
columns: string[]
|
||||
rows: Record<string, unknown>[]
|
||||
totalRows: string | null
|
||||
jobComplete: boolean
|
||||
totalBytesProcessed: string | null
|
||||
cacheHit: boolean | null
|
||||
jobReference: GoogleBigQueryJobReference | null
|
||||
pageToken: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface GoogleBigQueryListDatasetsResponse extends ToolResponse {
|
||||
output: {
|
||||
datasets: Array<{
|
||||
datasetId: string
|
||||
projectId: string
|
||||
friendlyName: string | null
|
||||
location: string | null
|
||||
}>
|
||||
nextPageToken: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface GoogleBigQueryListTablesResponse extends ToolResponse {
|
||||
output: {
|
||||
tables: Array<{
|
||||
tableId: string
|
||||
datasetId: string
|
||||
projectId: string
|
||||
type: string | null
|
||||
friendlyName: string | null
|
||||
creationTime: string | null
|
||||
}>
|
||||
totalItems: number | null
|
||||
nextPageToken: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface GoogleBigQueryGetTableResponse extends ToolResponse {
|
||||
output: {
|
||||
tableId: string
|
||||
datasetId: string
|
||||
projectId: string
|
||||
type: string | null
|
||||
description: string | null
|
||||
numRows: string | null
|
||||
numBytes: string | null
|
||||
schema: Array<{
|
||||
name: string
|
||||
type: string
|
||||
mode: string | null
|
||||
description: string | null
|
||||
}>
|
||||
creationTime: string | null
|
||||
lastModifiedTime: string | null
|
||||
location: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface GoogleBigQueryInsertRowsResponse extends ToolResponse {
|
||||
output: {
|
||||
insertedRows: number
|
||||
errors: Array<{
|
||||
index: number
|
||||
errors: Array<{
|
||||
reason: string | null
|
||||
location: string | null
|
||||
message: string | null
|
||||
}>
|
||||
}>
|
||||
}
|
||||
}
|
||||
133
apps/sim/tools/google_tasks/create.ts
Normal file
133
apps/sim/tools/google_tasks/create.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
import type { GoogleTasksCreateParams, GoogleTasksResponse } from '@/tools/google_tasks/types'
|
||||
import { TASKS_API_BASE } from '@/tools/google_tasks/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const createTool: ToolConfig<GoogleTasksCreateParams, GoogleTasksResponse> = {
|
||||
id: 'google_tasks_create',
|
||||
name: 'Google Tasks Create Task',
|
||||
description: 'Create a new task in a Google Tasks list',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-tasks',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'Google Tasks OAuth access token',
|
||||
},
|
||||
taskListId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Task list ID (defaults to primary task list "@default")',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Title of the task (max 1024 characters)',
|
||||
},
|
||||
notes: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Notes/description for the task (max 8192 characters)',
|
||||
},
|
||||
due: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Due date in RFC 3339 format (e.g., 2025-06-03T00:00:00.000Z)',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Task status: "needsAction" or "completed"',
|
||||
},
|
||||
parent: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Parent task ID to create this task as a subtask. Omit for top-level tasks.',
|
||||
},
|
||||
previous: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Previous sibling task ID to position after. Omit to place first among siblings.',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const taskListId = params.taskListId || '@default'
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.parent) queryParams.set('parent', params.parent)
|
||||
if (params.previous) queryParams.set('previous', params.previous)
|
||||
const qs = queryParams.toString()
|
||||
return `${TASKS_API_BASE}/lists/${encodeURIComponent(taskListId)}/tasks${qs ? `?${qs}` : ''}`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {
|
||||
title: params.title,
|
||||
}
|
||||
if (params.notes) body.notes = params.notes
|
||||
if (params.due) body.due = params.due
|
||||
if (params.status) body.status = params.status
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error?.message ?? 'Failed to create task')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? null,
|
||||
title: data.title ?? null,
|
||||
notes: data.notes ?? null,
|
||||
status: data.status ?? null,
|
||||
due: data.due ?? null,
|
||||
updated: data.updated ?? null,
|
||||
selfLink: data.selfLink ?? null,
|
||||
webViewLink: data.webViewLink ?? null,
|
||||
parent: data.parent ?? null,
|
||||
position: data.position ?? null,
|
||||
completed: data.completed ?? null,
|
||||
deleted: data.deleted ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Task ID' },
|
||||
title: { type: 'string', description: 'Task title' },
|
||||
notes: { type: 'string', description: 'Task notes', optional: true },
|
||||
status: { type: 'string', description: 'Task status (needsAction or completed)' },
|
||||
due: { type: 'string', description: 'Due date', optional: true },
|
||||
updated: { type: 'string', description: 'Last modification time' },
|
||||
selfLink: { type: 'string', description: 'URL for the task' },
|
||||
webViewLink: { type: 'string', description: 'Link to task in Google Tasks UI', optional: true },
|
||||
parent: { type: 'string', description: 'Parent task ID', optional: true },
|
||||
position: { type: 'string', description: 'Position among sibling tasks' },
|
||||
completed: { type: 'string', description: 'Completion date', optional: true },
|
||||
deleted: { type: 'boolean', description: 'Whether the task is deleted', optional: true },
|
||||
},
|
||||
}
|
||||
67
apps/sim/tools/google_tasks/delete.ts
Normal file
67
apps/sim/tools/google_tasks/delete.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import type { GoogleTasksDeleteParams, GoogleTasksDeleteResponse } from '@/tools/google_tasks/types'
|
||||
import { TASKS_API_BASE } from '@/tools/google_tasks/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const deleteTool: ToolConfig<GoogleTasksDeleteParams, GoogleTasksDeleteResponse> = {
|
||||
id: 'google_tasks_delete',
|
||||
name: 'Google Tasks Delete Task',
|
||||
description: 'Delete a task from a Google Tasks list',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-tasks',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'Google Tasks OAuth access token',
|
||||
},
|
||||
taskListId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Task list ID (defaults to primary task list "@default")',
|
||||
},
|
||||
taskId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the task to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const taskListId = params.taskListId || '@default'
|
||||
return `${TASKS_API_BASE}/lists/${encodeURIComponent(taskListId)}/tasks/${encodeURIComponent(params.taskId)}`
|
||||
},
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params) => {
|
||||
if (response.status === 204 || response.ok) {
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
taskId: params?.taskId || '',
|
||||
deleted: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
throw new Error(data.error?.message ?? 'Failed to delete task')
|
||||
},
|
||||
|
||||
outputs: {
|
||||
taskId: { type: 'string', description: 'Deleted task ID' },
|
||||
deleted: { type: 'boolean', description: 'Whether deletion was successful' },
|
||||
},
|
||||
}
|
||||
88
apps/sim/tools/google_tasks/get.ts
Normal file
88
apps/sim/tools/google_tasks/get.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import type { GoogleTasksGetParams, GoogleTasksResponse } from '@/tools/google_tasks/types'
|
||||
import { TASKS_API_BASE } from '@/tools/google_tasks/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const getTool: ToolConfig<GoogleTasksGetParams, GoogleTasksResponse> = {
|
||||
id: 'google_tasks_get',
|
||||
name: 'Google Tasks Get Task',
|
||||
description: 'Retrieve a specific task by ID from a Google Tasks list',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-tasks',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'Google Tasks OAuth access token',
|
||||
},
|
||||
taskListId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Task list ID (defaults to primary task list "@default")',
|
||||
},
|
||||
taskId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the task to retrieve',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const taskListId = params.taskListId || '@default'
|
||||
return `${TASKS_API_BASE}/lists/${encodeURIComponent(taskListId)}/tasks/${encodeURIComponent(params.taskId)}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error?.message ?? 'Failed to get task')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? null,
|
||||
title: data.title ?? null,
|
||||
notes: data.notes ?? null,
|
||||
status: data.status ?? null,
|
||||
due: data.due ?? null,
|
||||
updated: data.updated ?? null,
|
||||
selfLink: data.selfLink ?? null,
|
||||
webViewLink: data.webViewLink ?? null,
|
||||
parent: data.parent ?? null,
|
||||
position: data.position ?? null,
|
||||
completed: data.completed ?? null,
|
||||
deleted: data.deleted ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Task ID' },
|
||||
title: { type: 'string', description: 'Task title' },
|
||||
notes: { type: 'string', description: 'Task notes', optional: true },
|
||||
status: { type: 'string', description: 'Task status (needsAction or completed)' },
|
||||
due: { type: 'string', description: 'Due date', optional: true },
|
||||
updated: { type: 'string', description: 'Last modification time' },
|
||||
selfLink: { type: 'string', description: 'URL for the task' },
|
||||
webViewLink: { type: 'string', description: 'Link to task in Google Tasks UI', optional: true },
|
||||
parent: { type: 'string', description: 'Parent task ID', optional: true },
|
||||
position: { type: 'string', description: 'Position among sibling tasks' },
|
||||
completed: { type: 'string', description: 'Completion date', optional: true },
|
||||
deleted: { type: 'boolean', description: 'Whether the task is deleted', optional: true },
|
||||
},
|
||||
}
|
||||
6
apps/sim/tools/google_tasks/index.ts
Normal file
6
apps/sim/tools/google_tasks/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export { createTool as googleTasksCreateTool } from '@/tools/google_tasks/create'
|
||||
export { deleteTool as googleTasksDeleteTool } from '@/tools/google_tasks/delete'
|
||||
export { getTool as googleTasksGetTool } from '@/tools/google_tasks/get'
|
||||
export { listTool as googleTasksListTool } from '@/tools/google_tasks/list'
|
||||
export { listTaskListsTool as googleTasksListTaskListsTool } from '@/tools/google_tasks/list_task_lists'
|
||||
export { updateTool as googleTasksUpdateTool } from '@/tools/google_tasks/update'
|
||||
214
apps/sim/tools/google_tasks/list.ts
Normal file
214
apps/sim/tools/google_tasks/list.ts
Normal file
@@ -0,0 +1,214 @@
|
||||
import type { GoogleTasksListParams, GoogleTasksListResponse } from '@/tools/google_tasks/types'
|
||||
import { TASKS_API_BASE } from '@/tools/google_tasks/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listTool: ToolConfig<GoogleTasksListParams, GoogleTasksListResponse> = {
|
||||
id: 'google_tasks_list',
|
||||
name: 'Google Tasks List Tasks',
|
||||
description: 'List all tasks in a Google Tasks list',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-tasks',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'Google Tasks OAuth access token',
|
||||
},
|
||||
taskListId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Task list ID (defaults to primary task list "@default")',
|
||||
},
|
||||
maxResults: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of tasks to return (default 20, max 100)',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Token for pagination',
|
||||
},
|
||||
showCompleted: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to show completed tasks (default true)',
|
||||
},
|
||||
showDeleted: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to show deleted tasks (default false)',
|
||||
},
|
||||
showHidden: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to show hidden tasks (default false)',
|
||||
},
|
||||
dueMin: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lower bound for due date filter (RFC 3339 timestamp)',
|
||||
},
|
||||
dueMax: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Upper bound for due date filter (RFC 3339 timestamp)',
|
||||
},
|
||||
completedMin: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lower bound for task completion date (RFC 3339 timestamp)',
|
||||
},
|
||||
completedMax: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Upper bound for task completion date (RFC 3339 timestamp)',
|
||||
},
|
||||
updatedMin: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lower bound for last modification time (RFC 3339 timestamp)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const taskListId = params.taskListId || '@default'
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.maxResults) queryParams.set('maxResults', String(params.maxResults))
|
||||
if (params.pageToken) queryParams.set('pageToken', params.pageToken)
|
||||
if (params.showCompleted !== undefined)
|
||||
queryParams.set('showCompleted', String(params.showCompleted))
|
||||
if (params.showDeleted !== undefined)
|
||||
queryParams.set('showDeleted', String(params.showDeleted))
|
||||
if (params.showHidden !== undefined) queryParams.set('showHidden', String(params.showHidden))
|
||||
if (params.dueMin) queryParams.set('dueMin', params.dueMin)
|
||||
if (params.dueMax) queryParams.set('dueMax', params.dueMax)
|
||||
if (params.completedMin) queryParams.set('completedMin', params.completedMin)
|
||||
if (params.completedMax) queryParams.set('completedMax', params.completedMax)
|
||||
if (params.updatedMin) queryParams.set('updatedMin', params.updatedMin)
|
||||
const qs = queryParams.toString()
|
||||
return `${TASKS_API_BASE}/lists/${encodeURIComponent(taskListId)}/tasks${qs ? `?${qs}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error?.message ?? 'Failed to list tasks')
|
||||
}
|
||||
|
||||
const items = data.items ?? []
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
tasks: items.map((item: Record<string, unknown>) => ({
|
||||
id: (item.id as string) ?? null,
|
||||
title: (item.title as string) ?? null,
|
||||
notes: (item.notes as string) ?? null,
|
||||
status: (item.status as string) ?? null,
|
||||
due: (item.due as string) ?? null,
|
||||
completed: (item.completed as string) ?? null,
|
||||
updated: (item.updated as string) ?? null,
|
||||
selfLink: (item.selfLink as string) ?? null,
|
||||
webViewLink: (item.webViewLink as string) ?? null,
|
||||
parent: (item.parent as string) ?? null,
|
||||
position: (item.position as string) ?? null,
|
||||
hidden: (item.hidden as boolean) ?? null,
|
||||
deleted: (item.deleted as boolean) ?? null,
|
||||
links: Array.isArray(item.links)
|
||||
? (item.links as Array<Record<string, string>>).map((link) => ({
|
||||
type: link.type ?? '',
|
||||
description: link.description ?? '',
|
||||
link: link.link ?? '',
|
||||
}))
|
||||
: [],
|
||||
})),
|
||||
nextPageToken: data.nextPageToken ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
tasks: {
|
||||
type: 'array',
|
||||
description: 'List of tasks',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Task identifier' },
|
||||
title: { type: 'string', description: 'Title of the task' },
|
||||
notes: { type: 'string', description: 'Notes/description for the task', optional: true },
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Task status: "needsAction" or "completed"',
|
||||
},
|
||||
due: { type: 'string', description: 'Due date (RFC 3339 timestamp)', optional: true },
|
||||
completed: {
|
||||
type: 'string',
|
||||
description: 'Completion date (RFC 3339 timestamp)',
|
||||
optional: true,
|
||||
},
|
||||
updated: { type: 'string', description: 'Last modification time (RFC 3339 timestamp)' },
|
||||
selfLink: { type: 'string', description: 'URL pointing to this task' },
|
||||
webViewLink: {
|
||||
type: 'string',
|
||||
description: 'Link to task in Google Tasks UI',
|
||||
optional: true,
|
||||
},
|
||||
parent: { type: 'string', description: 'Parent task identifier', optional: true },
|
||||
position: {
|
||||
type: 'string',
|
||||
description: 'Position among sibling tasks (string-based ordering)',
|
||||
},
|
||||
hidden: { type: 'boolean', description: 'Whether the task is hidden', optional: true },
|
||||
deleted: { type: 'boolean', description: 'Whether the task is deleted', optional: true },
|
||||
links: {
|
||||
type: 'array',
|
||||
description: 'Collection of links associated with the task',
|
||||
optional: true,
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
type: {
|
||||
type: 'string',
|
||||
description: 'Link type (e.g., "email", "generic", "chat_message")',
|
||||
},
|
||||
description: { type: 'string', description: 'Link description' },
|
||||
link: { type: 'string', description: 'The URL' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
nextPageToken: {
|
||||
type: 'string',
|
||||
description: 'Token for retrieving the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
103
apps/sim/tools/google_tasks/list_task_lists.ts
Normal file
103
apps/sim/tools/google_tasks/list_task_lists.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import type {
|
||||
GoogleTasksListTaskListsParams,
|
||||
GoogleTasksListTaskListsResponse,
|
||||
} from '@/tools/google_tasks/types'
|
||||
import { TASKS_API_BASE } from '@/tools/google_tasks/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listTaskListsTool: ToolConfig<
|
||||
GoogleTasksListTaskListsParams,
|
||||
GoogleTasksListTaskListsResponse
|
||||
> = {
|
||||
id: 'google_tasks_list_task_lists',
|
||||
name: 'Google Tasks List Task Lists',
|
||||
description: 'Retrieve all task lists for the authenticated user',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-tasks',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'Google Tasks OAuth access token',
|
||||
},
|
||||
maxResults: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of task lists to return (default 20, max 100)',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Token for pagination',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.maxResults) queryParams.set('maxResults', String(params.maxResults))
|
||||
if (params.pageToken) queryParams.set('pageToken', params.pageToken)
|
||||
const qs = queryParams.toString()
|
||||
return `${TASKS_API_BASE}/users/@me/lists${qs ? `?${qs}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error?.message ?? 'Failed to list task lists')
|
||||
}
|
||||
|
||||
const items = data.items ?? []
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
taskLists: items.map((item: Record<string, unknown>) => ({
|
||||
id: (item.id as string) ?? null,
|
||||
title: (item.title as string) ?? null,
|
||||
updated: (item.updated as string) ?? null,
|
||||
selfLink: (item.selfLink as string) ?? null,
|
||||
})),
|
||||
nextPageToken: data.nextPageToken ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
taskLists: {
|
||||
type: 'array',
|
||||
description: 'List of task lists',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Task list identifier' },
|
||||
title: { type: 'string', description: 'Title of the task list' },
|
||||
updated: {
|
||||
type: 'string',
|
||||
description: 'Last modification time (RFC 3339 timestamp)',
|
||||
},
|
||||
selfLink: { type: 'string', description: 'URL pointing to this task list' },
|
||||
},
|
||||
},
|
||||
},
|
||||
nextPageToken: {
|
||||
type: 'string',
|
||||
description: 'Token for retrieving the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
113
apps/sim/tools/google_tasks/types.ts
Normal file
113
apps/sim/tools/google_tasks/types.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
export const TASKS_API_BASE = 'https://tasks.googleapis.com/tasks/v1'
|
||||
|
||||
export interface BaseGoogleTasksParams {
|
||||
accessToken: string
|
||||
}
|
||||
|
||||
export interface GoogleTasksListTaskListsParams extends BaseGoogleTasksParams {
|
||||
maxResults?: number
|
||||
pageToken?: string
|
||||
}
|
||||
|
||||
export interface GoogleTasksCreateParams extends BaseGoogleTasksParams {
|
||||
taskListId?: string
|
||||
title: string
|
||||
notes?: string
|
||||
due?: string
|
||||
status?: string
|
||||
parent?: string
|
||||
previous?: string
|
||||
}
|
||||
|
||||
export interface GoogleTasksListParams extends BaseGoogleTasksParams {
|
||||
taskListId?: string
|
||||
maxResults?: number
|
||||
pageToken?: string
|
||||
showCompleted?: boolean
|
||||
showDeleted?: boolean
|
||||
showHidden?: boolean
|
||||
dueMin?: string
|
||||
dueMax?: string
|
||||
completedMin?: string
|
||||
completedMax?: string
|
||||
updatedMin?: string
|
||||
}
|
||||
|
||||
export interface GoogleTasksGetParams extends BaseGoogleTasksParams {
|
||||
taskListId?: string
|
||||
taskId: string
|
||||
}
|
||||
|
||||
export interface GoogleTasksUpdateParams extends BaseGoogleTasksParams {
|
||||
taskListId?: string
|
||||
taskId: string
|
||||
title?: string
|
||||
notes?: string
|
||||
due?: string
|
||||
status?: string
|
||||
}
|
||||
|
||||
export interface GoogleTasksDeleteParams extends BaseGoogleTasksParams {
|
||||
taskListId?: string
|
||||
taskId: string
|
||||
}
|
||||
|
||||
export interface GoogleTasksResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string | null
|
||||
title: string | null
|
||||
notes: string | null
|
||||
status: string | null
|
||||
due: string | null
|
||||
updated: string | null
|
||||
selfLink: string | null
|
||||
webViewLink: string | null
|
||||
parent: string | null
|
||||
position: string | null
|
||||
completed: string | null
|
||||
deleted: boolean | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface GoogleTasksListResponse extends ToolResponse {
|
||||
output: {
|
||||
tasks: Array<{
|
||||
id: string | null
|
||||
title: string | null
|
||||
notes: string | null
|
||||
status: string | null
|
||||
due: string | null
|
||||
completed: string | null
|
||||
updated: string | null
|
||||
selfLink: string | null
|
||||
webViewLink: string | null
|
||||
parent: string | null
|
||||
position: string | null
|
||||
hidden: boolean | null
|
||||
deleted: boolean | null
|
||||
links: Array<{ type: string; description: string; link: string }>
|
||||
}>
|
||||
nextPageToken: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface GoogleTasksListTaskListsResponse extends ToolResponse {
|
||||
output: {
|
||||
taskLists: Array<{
|
||||
id: string | null
|
||||
title: string | null
|
||||
updated: string | null
|
||||
selfLink: string | null
|
||||
}>
|
||||
nextPageToken: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface GoogleTasksDeleteResponse extends ToolResponse {
|
||||
output: {
|
||||
taskId: string
|
||||
deleted: boolean
|
||||
}
|
||||
}
|
||||
121
apps/sim/tools/google_tasks/update.ts
Normal file
121
apps/sim/tools/google_tasks/update.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import type { GoogleTasksResponse, GoogleTasksUpdateParams } from '@/tools/google_tasks/types'
|
||||
import { TASKS_API_BASE } from '@/tools/google_tasks/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const updateTool: ToolConfig<GoogleTasksUpdateParams, GoogleTasksResponse> = {
|
||||
id: 'google_tasks_update',
|
||||
name: 'Google Tasks Update Task',
|
||||
description: 'Update an existing task in a Google Tasks list',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-tasks',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'Google Tasks OAuth access token',
|
||||
},
|
||||
taskListId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Task list ID (defaults to primary task list "@default")',
|
||||
},
|
||||
taskId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the task to update',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New title for the task',
|
||||
},
|
||||
notes: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New notes for the task',
|
||||
},
|
||||
due: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New due date in RFC 3339 format',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New status: "needsAction" or "completed"',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const taskListId = params.taskListId || '@default'
|
||||
return `${TASKS_API_BASE}/lists/${encodeURIComponent(taskListId)}/tasks/${encodeURIComponent(params.taskId)}`
|
||||
},
|
||||
method: 'PATCH',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {}
|
||||
if (params.title !== undefined) body.title = params.title
|
||||
if (params.notes !== undefined) body.notes = params.notes
|
||||
if (params.due !== undefined) body.due = params.due
|
||||
if (params.status !== undefined) body.status = params.status
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error?.message ?? 'Failed to update task')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? null,
|
||||
title: data.title ?? null,
|
||||
notes: data.notes ?? null,
|
||||
status: data.status ?? null,
|
||||
due: data.due ?? null,
|
||||
updated: data.updated ?? null,
|
||||
selfLink: data.selfLink ?? null,
|
||||
webViewLink: data.webViewLink ?? null,
|
||||
parent: data.parent ?? null,
|
||||
position: data.position ?? null,
|
||||
completed: data.completed ?? null,
|
||||
deleted: data.deleted ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Task ID' },
|
||||
title: { type: 'string', description: 'Task title' },
|
||||
notes: { type: 'string', description: 'Task notes', optional: true },
|
||||
status: { type: 'string', description: 'Task status (needsAction or completed)' },
|
||||
due: { type: 'string', description: 'Due date', optional: true },
|
||||
updated: { type: 'string', description: 'Last modification time' },
|
||||
selfLink: { type: 'string', description: 'URL for the task' },
|
||||
webViewLink: { type: 'string', description: 'Link to task in Google Tasks UI', optional: true },
|
||||
parent: { type: 'string', description: 'Parent task ID', optional: true },
|
||||
position: { type: 'string', description: 'Position among sibling tasks' },
|
||||
completed: { type: 'string', description: 'Completion date', optional: true },
|
||||
deleted: { type: 'boolean', description: 'Whether the task is deleted', optional: true },
|
||||
},
|
||||
}
|
||||
@@ -190,17 +190,25 @@ import {
|
||||
confluenceCreateCommentTool,
|
||||
confluenceCreatePagePropertyTool,
|
||||
confluenceCreatePageTool,
|
||||
confluenceCreateSpacePropertyTool,
|
||||
confluenceCreateSpaceTool,
|
||||
confluenceDeleteAttachmentTool,
|
||||
confluenceDeleteBlogPostTool,
|
||||
confluenceDeleteCommentTool,
|
||||
confluenceDeleteLabelTool,
|
||||
confluenceDeletePagePropertyTool,
|
||||
confluenceDeletePageTool,
|
||||
confluenceDeleteSpacePropertyTool,
|
||||
confluenceDeleteSpaceTool,
|
||||
confluenceGetBlogPostTool,
|
||||
confluenceGetPageAncestorsTool,
|
||||
confluenceGetPageChildrenTool,
|
||||
confluenceGetPageDescendantsTool,
|
||||
confluenceGetPagesByLabelTool,
|
||||
confluenceGetPageVersionTool,
|
||||
confluenceGetSpaceTool,
|
||||
confluenceGetTaskTool,
|
||||
confluenceGetUserTool,
|
||||
confluenceListAttachmentsTool,
|
||||
confluenceListBlogPostsInSpaceTool,
|
||||
confluenceListBlogPostsTool,
|
||||
@@ -210,11 +218,17 @@ import {
|
||||
confluenceListPagesInSpaceTool,
|
||||
confluenceListPageVersionsTool,
|
||||
confluenceListSpaceLabelsTool,
|
||||
confluenceListSpacePermissionsTool,
|
||||
confluenceListSpacePropertiesTool,
|
||||
confluenceListSpacesTool,
|
||||
confluenceListTasksTool,
|
||||
confluenceRetrieveTool,
|
||||
confluenceSearchInSpaceTool,
|
||||
confluenceSearchTool,
|
||||
confluenceUpdateBlogPostTool,
|
||||
confluenceUpdateCommentTool,
|
||||
confluenceUpdateSpaceTool,
|
||||
confluenceUpdateTaskTool,
|
||||
confluenceUpdateTool,
|
||||
confluenceUploadAttachmentTool,
|
||||
} from '@/tools/confluence'
|
||||
@@ -248,6 +262,12 @@ import {
|
||||
datadogSendLogsTool,
|
||||
datadogSubmitMetricsTool,
|
||||
} from '@/tools/datadog'
|
||||
import {
|
||||
devinCreateSessionTool,
|
||||
devinGetSessionTool,
|
||||
devinListSessionsTool,
|
||||
devinSendMessageTool,
|
||||
} from '@/tools/devin'
|
||||
import {
|
||||
discordAddReactionTool,
|
||||
discordArchiveThreadTool,
|
||||
@@ -630,6 +650,13 @@ import {
|
||||
gongLookupPhoneTool,
|
||||
} from '@/tools/gong'
|
||||
import { googleSearchTool } from '@/tools/google'
|
||||
import {
|
||||
googleBigQueryGetTableTool,
|
||||
googleBigQueryInsertRowsTool,
|
||||
googleBigQueryListDatasetsTool,
|
||||
googleBigQueryListTablesTool,
|
||||
googleBigQueryQueryTool,
|
||||
} from '@/tools/google_bigquery'
|
||||
import { googleBooksVolumeDetailsTool, googleBooksVolumeSearchTool } from '@/tools/google_books'
|
||||
import {
|
||||
googleCalendarCreateTool,
|
||||
@@ -755,6 +782,14 @@ import {
|
||||
googleSlidesUpdateSlidesPositionTool,
|
||||
googleSlidesWriteTool,
|
||||
} from '@/tools/google_slides'
|
||||
import {
|
||||
googleTasksCreateTool,
|
||||
googleTasksDeleteTool,
|
||||
googleTasksGetTool,
|
||||
googleTasksListTaskListsTool,
|
||||
googleTasksListTool,
|
||||
googleTasksUpdateTool,
|
||||
} from '@/tools/google_tasks'
|
||||
import { googleTranslateDetectTool, googleTranslateTool } from '@/tools/google_translate'
|
||||
import {
|
||||
createMattersExportTool,
|
||||
@@ -2556,6 +2591,10 @@ export const tools: Record<string, ToolConfig> = {
|
||||
dropbox_get_metadata: dropboxGetMetadataTool,
|
||||
dropbox_create_shared_link: dropboxCreateSharedLinkTool,
|
||||
dropbox_search: dropboxSearchTool,
|
||||
devin_create_session: devinCreateSessionTool,
|
||||
devin_get_session: devinGetSessionTool,
|
||||
devin_list_sessions: devinListSessionsTool,
|
||||
devin_send_message: devinSendMessageTool,
|
||||
duckduckgo_search: duckduckgoSearchTool,
|
||||
dspy_predict: predictTool,
|
||||
dspy_chain_of_thought: chainOfThoughtTool,
|
||||
@@ -2921,6 +2960,12 @@ export const tools: Record<string, ToolConfig> = {
|
||||
google_maps_speed_limits: googleMapsSpeedLimitsTool,
|
||||
google_maps_timezone: googleMapsTimezoneTool,
|
||||
google_maps_validate_address: googleMapsValidateAddressTool,
|
||||
google_tasks_create: googleTasksCreateTool,
|
||||
google_tasks_delete: googleTasksDeleteTool,
|
||||
google_tasks_get: googleTasksGetTool,
|
||||
google_tasks_list: googleTasksListTool,
|
||||
google_tasks_list_task_lists: googleTasksListTaskListsTool,
|
||||
google_tasks_update: googleTasksUpdateTool,
|
||||
google_translate_detect: googleTranslateDetectTool,
|
||||
google_translate_text: googleTranslateTool,
|
||||
google_sheets_read: googleSheetsReadTool,
|
||||
@@ -3032,8 +3077,22 @@ export const tools: Record<string, ToolConfig> = {
|
||||
confluence_list_space_labels: confluenceListSpaceLabelsTool,
|
||||
confluence_delete_label: confluenceDeleteLabelTool,
|
||||
confluence_delete_page_property: confluenceDeletePagePropertyTool,
|
||||
confluence_get_page_descendants: confluenceGetPageDescendantsTool,
|
||||
confluence_get_space: confluenceGetSpaceTool,
|
||||
confluence_create_space: confluenceCreateSpaceTool,
|
||||
confluence_update_space: confluenceUpdateSpaceTool,
|
||||
confluence_delete_space: confluenceDeleteSpaceTool,
|
||||
confluence_get_user: confluenceGetUserTool,
|
||||
confluence_list_spaces: confluenceListSpacesTool,
|
||||
confluence_update_blogpost: confluenceUpdateBlogPostTool,
|
||||
confluence_delete_blogpost: confluenceDeleteBlogPostTool,
|
||||
confluence_list_tasks: confluenceListTasksTool,
|
||||
confluence_get_task: confluenceGetTaskTool,
|
||||
confluence_update_task: confluenceUpdateTaskTool,
|
||||
confluence_list_space_permissions: confluenceListSpacePermissionsTool,
|
||||
confluence_list_space_properties: confluenceListSpacePropertiesTool,
|
||||
confluence_create_space_property: confluenceCreateSpacePropertyTool,
|
||||
confluence_delete_space_property: confluenceDeleteSpacePropertyTool,
|
||||
cursor_list_agents: cursorListAgentsTool,
|
||||
cursor_list_agents_v2: cursorListAgentsV2Tool,
|
||||
cursor_get_agent: cursorGetAgentTool,
|
||||
@@ -3593,6 +3652,11 @@ export const tools: Record<string, ToolConfig> = {
|
||||
wordpress_list_users: wordpressListUsersTool,
|
||||
wordpress_get_user: wordpressGetUserTool,
|
||||
wordpress_search_content: wordpressSearchContentTool,
|
||||
google_bigquery_query: googleBigQueryQueryTool,
|
||||
google_bigquery_list_datasets: googleBigQueryListDatasetsTool,
|
||||
google_bigquery_list_tables: googleBigQueryListTablesTool,
|
||||
google_bigquery_get_table: googleBigQueryGetTableTool,
|
||||
google_bigquery_insert_rows: googleBigQueryInsertRowsTool,
|
||||
google_vault_create_matters_export: createMattersExportTool,
|
||||
google_vault_list_matters_export: listMattersExportTool,
|
||||
google_vault_create_matters_holds: createMattersHoldsTool,
|
||||
|
||||
16
bun.lock
16
bun.lock
@@ -13,7 +13,7 @@
|
||||
"glob": "13.0.0",
|
||||
"husky": "9.1.7",
|
||||
"lint-staged": "16.0.0",
|
||||
"turbo": "2.8.10",
|
||||
"turbo": "2.8.11",
|
||||
},
|
||||
},
|
||||
"apps/docs": {
|
||||
@@ -3437,19 +3437,19 @@
|
||||
|
||||
"tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="],
|
||||
|
||||
"turbo": ["turbo@2.8.10", "", { "optionalDependencies": { "turbo-darwin-64": "2.8.10", "turbo-darwin-arm64": "2.8.10", "turbo-linux-64": "2.8.10", "turbo-linux-arm64": "2.8.10", "turbo-windows-64": "2.8.10", "turbo-windows-arm64": "2.8.10" }, "bin": { "turbo": "bin/turbo" } }, "sha512-OxbzDES66+x7nnKGg2MwBA1ypVsZoDTLHpeaP4giyiHSixbsiTaMyeJqbEyvBdp5Cm28fc+8GG6RdQtic0ijwQ=="],
|
||||
"turbo": ["turbo@2.8.11", "", { "optionalDependencies": { "turbo-darwin-64": "2.8.11", "turbo-darwin-arm64": "2.8.11", "turbo-linux-64": "2.8.11", "turbo-linux-arm64": "2.8.11", "turbo-windows-64": "2.8.11", "turbo-windows-arm64": "2.8.11" }, "bin": { "turbo": "bin/turbo" } }, "sha512-H+rwSHHPLoyPOSoHdmI1zY0zy0GGj1Dmr7SeJW+nZiWLz2nex8EJ+fkdVabxXFMNEux+aywI4Sae8EqhmnOv4A=="],
|
||||
|
||||
"turbo-darwin-64": ["turbo-darwin-64@2.8.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-A03fXh+B7S8mL3PbdhTd+0UsaGrhfyPkODvzBDpKRY7bbeac4MDFpJ7I+Slf2oSkCEeSvHKR7Z4U71uKRUfX7g=="],
|
||||
"turbo-darwin-64": ["turbo-darwin-64@2.8.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-XKaCWaz4OCt77oYYvGCIRpvYD4c/aNaKjRkUpv+e8rN3RZb+5Xsyew4yRO+gaHdMIUhQznXNXfHlhs+/p7lIhA=="],
|
||||
|
||||
"turbo-darwin-arm64": ["turbo-darwin-arm64@2.8.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-sidzowgWL3s5xCHLeqwC9M3s9M0i16W1nuQF3Mc7fPHpZ+YPohvcbVFBB2uoRRHYZg6yBnwD4gyUHKTeXfwtXA=="],
|
||||
"turbo-darwin-arm64": ["turbo-darwin-arm64@2.8.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-VvynLHGUNvQ9k7GZjRPSsRcK4VkioTfFb7O7liAk4nHKjEcMdls7GqxzjVWgJiKz3hWmQGaP9hRa9UUnhVWCxA=="],
|
||||
|
||||
"turbo-linux-64": ["turbo-linux-64@2.8.10", "", { "os": "linux", "cpu": "x64" }, "sha512-YK9vcpL3TVtqonB021XwgaQhY9hJJbKKUhLv16osxV0HkcQASQWUqR56yMge7puh6nxU67rQlTq1b7ksR1T3KA=="],
|
||||
"turbo-linux-64": ["turbo-linux-64@2.8.11", "", { "os": "linux", "cpu": "x64" }, "sha512-cbSn37dcm+EmkQ7DD0euy7xV7o2el4GAOr1XujvkAyKjjNvQ+6QIUeDgQcwAx3D17zPpDvfDMJY2dLQadWnkmQ=="],
|
||||
|
||||
"turbo-linux-arm64": ["turbo-linux-arm64@2.8.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-3+j2tL0sG95iBJTm+6J8/45JsETQABPqtFyYjVjBbi6eVGdtNTiBmHNKrbvXRlQ3ZbUG75bKLaSSDHSEEN+btQ=="],
|
||||
"turbo-linux-arm64": ["turbo-linux-arm64@2.8.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-+trymp2s2aBrhS04l6qFxcExzZ8ffndevuUB9c5RCeqsVpZeiWuGQlWNm5XjOmzoMayxRARZ5ma7yiWbGMiLqQ=="],
|
||||
|
||||
"turbo-windows-64": ["turbo-windows-64@2.8.10", "", { "os": "win32", "cpu": "x64" }, "sha512-hdeF5qmVY/NFgiucf8FW0CWJWtyT2QPm5mIsX0W1DXAVzqKVXGq+Zf+dg4EUngAFKjDzoBeN6ec2Fhajwfztkw=="],
|
||||
"turbo-windows-64": ["turbo-windows-64@2.8.11", "", { "os": "win32", "cpu": "x64" }, "sha512-3kJjFSM4yw1n9Uzmi+XkAUgCae19l/bH6RJ442xo7mnZm0tpOjo33F+FYHoSVpIWVMd0HG0LDccyafPSdylQbA=="],
|
||||
|
||||
"turbo-windows-arm64": ["turbo-windows-arm64@2.8.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-QGdr/Q8LWmj+ITMkSvfiz2glf0d7JG0oXVzGL3jxkGqiBI1zXFj20oqVY0qWi+112LO9SVrYdpHS0E/oGFrMbQ=="],
|
||||
"turbo-windows-arm64": ["turbo-windows-arm64@2.8.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-JOM4uF2vuLsJUvibdR6X9QqdZr6BhC6Nhlrw4LKFPsXZZI/9HHLoqAiYRpE4MuzIwldCH/jVySnWXrI1SKto0g=="],
|
||||
|
||||
"tweetnacl": ["tweetnacl@0.14.5", "", {}, "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="],
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
"glob": "13.0.0",
|
||||
"husky": "9.1.7",
|
||||
"lint-staged": "16.0.0",
|
||||
"turbo": "2.8.10"
|
||||
"turbo": "2.8.11"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,ts,tsx,json,css,scss}": [
|
||||
|
||||
9
packages/db/migrations/0160_classy_tyger_tiger.sql
Normal file
9
packages/db/migrations/0160_classy_tyger_tiger.sql
Normal file
@@ -0,0 +1,9 @@
|
||||
DROP INDEX "a2a_agent_workspace_id_idx";--> statement-breakpoint
|
||||
DROP INDEX "a2a_push_notification_config_task_id_idx";--> statement-breakpoint
|
||||
DROP INDEX "credential_member_credential_id_idx";--> statement-breakpoint
|
||||
DROP INDEX "credential_set_organization_id_idx";--> statement-breakpoint
|
||||
DROP INDEX "credential_set_member_set_id_idx";--> statement-breakpoint
|
||||
DROP INDEX "permission_group_organization_id_idx";--> statement-breakpoint
|
||||
DROP INDEX "skill_workspace_id_idx";--> statement-breakpoint
|
||||
DROP INDEX "user_table_rows_workspace_id_idx";--> statement-breakpoint
|
||||
CREATE INDEX "workflow_execution_logs_running_started_at_idx" ON "workflow_execution_logs" USING btree ("started_at") WHERE status = 'running';
|
||||
12326
packages/db/migrations/meta/0160_snapshot.json
Normal file
12326
packages/db/migrations/meta/0160_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1114,6 +1114,13 @@
|
||||
"when": 1771897746619,
|
||||
"tag": "0159_magical_marten_broadcloak",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 160,
|
||||
"version": "7",
|
||||
"when": 1772138479896,
|
||||
"tag": "0160_classy_tyger_tiger",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -334,6 +334,9 @@ export const workflowExecutionLogs = pgTable(
|
||||
table.workspaceId,
|
||||
table.startedAt
|
||||
),
|
||||
runningStartedAtIdx: index('workflow_execution_logs_running_started_at_idx')
|
||||
.on(table.startedAt)
|
||||
.where(sql`status = 'running'`),
|
||||
})
|
||||
)
|
||||
|
||||
@@ -812,7 +815,6 @@ export const skill = pgTable(
|
||||
updatedAt: timestamp('updated_at').notNull().defaultNow(),
|
||||
},
|
||||
(table) => ({
|
||||
workspaceIdIdx: index('skill_workspace_id_idx').on(table.workspaceId),
|
||||
workspaceNameUnique: uniqueIndex('skill_workspace_name_unique').on(
|
||||
table.workspaceId,
|
||||
table.name
|
||||
@@ -1934,7 +1936,6 @@ export const a2aAgent = pgTable(
|
||||
updatedAt: timestamp('updated_at').notNull().defaultNow(),
|
||||
},
|
||||
(table) => ({
|
||||
workspaceIdIdx: index('a2a_agent_workspace_id_idx').on(table.workspaceId),
|
||||
workflowIdIdx: index('a2a_agent_workflow_id_idx').on(table.workflowId),
|
||||
createdByIdx: index('a2a_agent_created_by_idx').on(table.createdBy),
|
||||
workspaceWorkflowUnique: uniqueIndex('a2a_agent_workspace_workflow_unique').on(
|
||||
@@ -2018,7 +2019,6 @@ export const a2aPushNotificationConfig = pgTable(
|
||||
updatedAt: timestamp('updated_at').notNull().defaultNow(),
|
||||
},
|
||||
(table) => ({
|
||||
taskIdIdx: index('a2a_push_notification_config_task_id_idx').on(table.taskId),
|
||||
taskIdUnique: uniqueIndex('a2a_push_notification_config_task_unique').on(table.taskId),
|
||||
})
|
||||
)
|
||||
@@ -2173,7 +2173,6 @@ export const credentialMember = pgTable(
|
||||
updatedAt: timestamp('updated_at').notNull().defaultNow(),
|
||||
},
|
||||
(table) => ({
|
||||
credentialIdIdx: index('credential_member_credential_id_idx').on(table.credentialId),
|
||||
userIdIdx: index('credential_member_user_id_idx').on(table.userId),
|
||||
roleIdx: index('credential_member_role_idx').on(table.role),
|
||||
statusIdx: index('credential_member_status_idx').on(table.status),
|
||||
@@ -2224,7 +2223,6 @@ export const credentialSet = pgTable(
|
||||
updatedAt: timestamp('updated_at').notNull().defaultNow(),
|
||||
},
|
||||
(table) => ({
|
||||
organizationIdIdx: index('credential_set_organization_id_idx').on(table.organizationId),
|
||||
createdByIdx: index('credential_set_created_by_idx').on(table.createdBy),
|
||||
orgNameUnique: uniqueIndex('credential_set_org_name_unique').on(
|
||||
table.organizationId,
|
||||
@@ -2257,7 +2255,6 @@ export const credentialSetMember = pgTable(
|
||||
updatedAt: timestamp('updated_at').notNull().defaultNow(),
|
||||
},
|
||||
(table) => ({
|
||||
credentialSetIdIdx: index('credential_set_member_set_id_idx').on(table.credentialSetId),
|
||||
userIdIdx: index('credential_set_member_user_id_idx').on(table.userId),
|
||||
uniqueMembership: uniqueIndex('credential_set_member_unique').on(
|
||||
table.credentialSetId,
|
||||
@@ -2320,7 +2317,6 @@ export const permissionGroup = pgTable(
|
||||
autoAddNewMembers: boolean('auto_add_new_members').notNull().default(false),
|
||||
},
|
||||
(table) => ({
|
||||
organizationIdIdx: index('permission_group_organization_id_idx').on(table.organizationId),
|
||||
createdByIdx: index('permission_group_created_by_idx').on(table.createdBy),
|
||||
orgNameUnique: uniqueIndex('permission_group_org_name_unique').on(
|
||||
table.organizationId,
|
||||
@@ -2438,7 +2434,6 @@ export const userTableRows = pgTable(
|
||||
},
|
||||
(table) => ({
|
||||
tableIdIdx: index('user_table_rows_table_id_idx').on(table.tableId),
|
||||
workspaceIdIdx: index('user_table_rows_workspace_id_idx').on(table.workspaceId),
|
||||
dataGinIdx: index('user_table_rows_data_gin_idx').using('gin', table.data),
|
||||
workspaceTableIdx: index('user_table_rows_workspace_table_idx').on(
|
||||
table.workspaceId,
|
||||
|
||||
@@ -86,6 +86,8 @@ export const auditMock = {
|
||||
WORKFLOW_DEPLOYED: 'workflow.deployed',
|
||||
WORKFLOW_UNDEPLOYED: 'workflow.undeployed',
|
||||
WORKFLOW_DUPLICATED: 'workflow.duplicated',
|
||||
WORKFLOW_LOCKED: 'workflow.locked',
|
||||
WORKFLOW_UNLOCKED: 'workflow.unlocked',
|
||||
WORKFLOW_DEPLOYMENT_ACTIVATED: 'workflow.deployment_activated',
|
||||
WORKFLOW_DEPLOYMENT_REVERTED: 'workflow.deployment_reverted',
|
||||
WORKFLOW_VARIABLES_UPDATED: 'workflow.variables_updated',
|
||||
|
||||
Reference in New Issue
Block a user