From ae7937280e7be4f3666148c4ae4b16e93e141572 Mon Sep 17 00:00:00 2001 From: Adam Gough <77861281+aadamgough@users.noreply.github.com> Date: Sat, 6 Dec 2025 17:32:30 -0800 Subject: [PATCH] feat(google-groups): added google groups (#2229) * added google-groups * added a few more endpoints * removed comments * removed named imports, fixed comments * fixed google groups tool names --------- Co-authored-by: aadamgough Co-authored-by: waleed --- apps/docs/components/icons.tsx | 32 ++ apps/docs/components/ui/icon-mapping.ts | 2 + .../content/docs/en/tools/google_groups.mdx | 221 ++++++++++++ apps/docs/content/docs/en/tools/meta.json | 1 + .../components/oauth-required-modal.tsx | 6 + apps/sim/blocks/blocks/google_groups.ts | 317 ++++++++++++++++++ apps/sim/blocks/registry.ts | 2 + apps/sim/components/icons.tsx | 32 ++ apps/sim/lib/auth/auth.ts | 16 + apps/sim/lib/oauth/oauth.ts | 15 + apps/sim/tools/dynamodb/index.ts | 7 +- apps/sim/tools/firecrawl/index.ts | 6 +- apps/sim/tools/google/index.ts | 4 +- apps/sim/tools/google_groups/add_member.ts | 72 ++++ apps/sim/tools/google_groups/create_group.ts | 73 ++++ apps/sim/tools/google_groups/delete_group.ts | 52 +++ apps/sim/tools/google_groups/get_group.ts | 52 +++ apps/sim/tools/google_groups/get_member.ts | 59 ++++ apps/sim/tools/google_groups/has_member.ts | 59 ++++ apps/sim/tools/google_groups/index.ts | 23 ++ apps/sim/tools/google_groups/list_groups.ts | 97 ++++++ apps/sim/tools/google_groups/list_members.ts | 84 +++++ apps/sim/tools/google_groups/remove_member.ts | 59 ++++ apps/sim/tools/google_groups/types.ts | 111 ++++++ apps/sim/tools/google_groups/update_group.ts | 85 +++++ apps/sim/tools/google_groups/update_member.ts | 70 ++++ apps/sim/tools/http/index.ts | 4 +- apps/sim/tools/jina/index.ts | 7 +- apps/sim/tools/mongodb/index.ts | 17 +- apps/sim/tools/mysql/index.ts | 17 +- apps/sim/tools/neo4j/index.ts | 19 +- apps/sim/tools/openai/index.ts | 3 +- apps/sim/tools/postgresql/index.ts | 6 +- apps/sim/tools/rds/index.ts | 6 +- apps/sim/tools/registry.ts | 152 +++++---- apps/sim/tools/serper/index.ts | 4 +- apps/sim/tools/ssh/index.ts | 41 ++- 37 files changed, 1725 insertions(+), 108 deletions(-) create mode 100644 apps/docs/content/docs/en/tools/google_groups.mdx create mode 100644 apps/sim/blocks/blocks/google_groups.ts create mode 100644 apps/sim/tools/google_groups/add_member.ts create mode 100644 apps/sim/tools/google_groups/create_group.ts create mode 100644 apps/sim/tools/google_groups/delete_group.ts create mode 100644 apps/sim/tools/google_groups/get_group.ts create mode 100644 apps/sim/tools/google_groups/get_member.ts create mode 100644 apps/sim/tools/google_groups/has_member.ts create mode 100644 apps/sim/tools/google_groups/index.ts create mode 100644 apps/sim/tools/google_groups/list_groups.ts create mode 100644 apps/sim/tools/google_groups/list_members.ts create mode 100644 apps/sim/tools/google_groups/remove_member.ts create mode 100644 apps/sim/tools/google_groups/types.ts create mode 100644 apps/sim/tools/google_groups/update_group.ts create mode 100644 apps/sim/tools/google_groups/update_member.ts diff --git a/apps/docs/components/icons.tsx b/apps/docs/components/icons.tsx index d6dc0ae4d..972597cf8 100644 --- a/apps/docs/components/icons.tsx +++ b/apps/docs/components/icons.tsx @@ -4089,3 +4089,35 @@ export function PolymarketIcon(props: SVGProps) { ) } + +export function GoogleGroupsIcon(props: SVGProps) { + return ( + + + + + + + + ) +} diff --git a/apps/docs/components/ui/icon-mapping.ts b/apps/docs/components/ui/icon-mapping.ts index a5267e9eb..235dc6642 100644 --- a/apps/docs/components/ui/icon-mapping.ts +++ b/apps/docs/components/ui/icon-mapping.ts @@ -32,6 +32,7 @@ import { GoogleDocsIcon, GoogleDriveIcon, GoogleFormsIcon, + GoogleGroupsIcon, GoogleIcon, GoogleSheetsIcon, GoogleSlidesIcon, @@ -195,6 +196,7 @@ export const blockTypeToIconMap: Record = { google_vault: GoogleVaultIcon, google_slides: GoogleSlidesIcon, google_sheets: GoogleSheetsIcon, + google_groups: GoogleGroupsIcon, google_forms: GoogleFormsIcon, google_drive: GoogleDriveIcon, google_docs: GoogleDocsIcon, diff --git a/apps/docs/content/docs/en/tools/google_groups.mdx b/apps/docs/content/docs/en/tools/google_groups.mdx new file mode 100644 index 000000000..727ab7271 --- /dev/null +++ b/apps/docs/content/docs/en/tools/google_groups.mdx @@ -0,0 +1,221 @@ +--- +title: Google Groups +description: Manage Google Workspace Groups and their members +--- + +import { BlockInfoCard } from "@/components/ui/block-info-card" + + + +## Usage Instructions + +Connect to Google Workspace to create, update, and manage groups and their members using the Admin SDK Directory API. + + + +## Tools + +### `google_groups_list_groups` + +List all groups in a Google Workspace domain + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `customer` | string | No | Customer ID or "my_customer" for the authenticated user\'s domain | +| `domain` | string | No | Domain name to filter groups by | +| `maxResults` | number | No | Maximum number of results to return \(1-200\) | +| `pageToken` | string | No | Token for pagination | +| `query` | string | No | Search query to filter groups \(e.g., "email:admin*"\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `output` | json | Google Groups API response data | + +### `google_groups_get_group` + +Get details of a specific Google Group by email or group ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `groupKey` | string | Yes | Group email address or unique group ID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `output` | json | Google Groups API response data | + +### `google_groups_create_group` + +Create a new Google Group in the domain + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | Yes | Email address for the new group \(e.g., team@yourdomain.com\) | +| `name` | string | Yes | Display name for the group | +| `description` | string | No | Description of the group | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `output` | json | Google Groups API response data | + +### `google_groups_update_group` + +Update an existing Google Group + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `groupKey` | string | Yes | Group email address or unique group ID | +| `name` | string | No | New display name for the group | +| `description` | string | No | New description for the group | +| `email` | string | No | New email address for the group | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `output` | json | Google Groups API response data | + +### `google_groups_delete_group` + +Delete a Google Group + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `groupKey` | string | Yes | Group email address or unique group ID to delete | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `output` | json | Google Groups API response data | + +### `google_groups_list_members` + +List all members of a Google Group + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `groupKey` | string | Yes | Group email address or unique group ID | +| `maxResults` | number | No | Maximum number of results to return \(1-200\) | +| `pageToken` | string | No | Token for pagination | +| `roles` | string | No | Filter by roles \(comma-separated: OWNER, MANAGER, MEMBER\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `output` | json | Google Groups API response data | + +### `google_groups_get_member` + +Get details of a specific member in a Google Group + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `groupKey` | string | Yes | Group email address or unique group ID | +| `memberKey` | string | Yes | Member email address or unique member ID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `output` | json | Google Groups API response data | + +### `google_groups_add_member` + +Add a new member to a Google Group + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `groupKey` | string | Yes | Group email address or unique group ID | +| `email` | string | Yes | Email address of the member to add | +| `role` | string | No | Role for the member \(MEMBER, MANAGER, or OWNER\). Defaults to MEMBER. | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `output` | json | Google Groups API response data | + +### `google_groups_remove_member` + +Remove a member from a Google Group + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `groupKey` | string | Yes | Group email address or unique group ID | +| `memberKey` | string | Yes | Email address or unique ID of the member to remove | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `output` | json | Google Groups API response data | + +### `google_groups_update_member` + +Update a member + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `groupKey` | string | Yes | Group email address or unique group ID | +| `memberKey` | string | Yes | Member email address or unique member ID | +| `role` | string | Yes | New role for the member \(MEMBER, MANAGER, or OWNER\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `output` | json | Google Groups API response data | + +### `google_groups_has_member` + +Check if a user is a member of a Google Group + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `groupKey` | string | Yes | Group email address or unique group ID | +| `memberKey` | string | Yes | Member email address or unique member ID to check | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `output` | json | Google Groups API response data | + + + +## Notes + +- Category: `tools` +- Type: `google_groups` diff --git a/apps/docs/content/docs/en/tools/meta.json b/apps/docs/content/docs/en/tools/meta.json index d50ec3bb6..60406fa4b 100644 --- a/apps/docs/content/docs/en/tools/meta.json +++ b/apps/docs/content/docs/en/tools/meta.json @@ -27,6 +27,7 @@ "google_docs", "google_drive", "google_forms", + "google_groups", "google_search", "google_sheets", "google_slides", diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal.tsx index 9d5eec3f5..a236fcd75 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal.tsx @@ -36,6 +36,12 @@ const SCOPE_DESCRIPTIONS: Record = { 'https://www.googleapis.com/auth/forms.responses.readonly': 'View responses to your Google Forms', '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', + 'https://www.googleapis.com/auth/admin.directory.group.member': + 'Manage Google Workspace group memberships', + 'https://www.googleapis.com/auth/admin.directory.group.readonly': 'View Google Workspace groups', + 'https://www.googleapis.com/auth/admin.directory.group.member.readonly': + 'View Google Workspace group memberships', 'read:confluence-content.all': 'Read all Confluence content', 'read:confluence-space.summary': 'Read Confluence space information', 'read:space:confluence': 'View Confluence spaces', diff --git a/apps/sim/blocks/blocks/google_groups.ts b/apps/sim/blocks/blocks/google_groups.ts new file mode 100644 index 000000000..658d66be7 --- /dev/null +++ b/apps/sim/blocks/blocks/google_groups.ts @@ -0,0 +1,317 @@ +import { GoogleGroupsIcon } from '@/components/icons' +import type { BlockConfig } from '@/blocks/types' +import { AuthMode } from '@/blocks/types' + +export const GoogleGroupsBlock: BlockConfig = { + type: 'google_groups', + name: 'Google Groups', + description: 'Manage Google Workspace Groups and their members', + authMode: AuthMode.OAuth, + longDescription: + 'Connect to Google Workspace to create, update, and manage groups and their members using the Admin SDK Directory API.', + docsLink: 'https://developers.google.com/admin-sdk/directory/v1/guides/manage-groups', + category: 'tools', + bgColor: '#E8F0FE', + icon: GoogleGroupsIcon, + subBlocks: [ + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + options: [ + { label: 'List Groups', id: 'list_groups' }, + { label: 'Get Group', id: 'get_group' }, + { label: 'Create Group', id: 'create_group' }, + { label: 'Update Group', id: 'update_group' }, + { label: 'Delete Group', id: 'delete_group' }, + { label: 'List Members', id: 'list_members' }, + { label: 'Get Member', id: 'get_member' }, + { label: 'Add Member', id: 'add_member' }, + { label: 'Update Member Role', id: 'update_member' }, + { label: 'Remove Member', id: 'remove_member' }, + { label: 'Check Membership', id: 'has_member' }, + ], + value: () => 'list_groups', + }, + { + id: 'credential', + title: 'Google Groups Account', + type: 'oauth-input', + required: true, + serviceId: 'google-groups', + requiredScopes: [ + 'https://www.googleapis.com/auth/admin.directory.group', + 'https://www.googleapis.com/auth/admin.directory.group.member', + ], + placeholder: 'Select Google Workspace account', + }, + + { + id: 'customer', + title: 'Customer ID', + type: 'short-input', + placeholder: 'my_customer (default)', + condition: { field: 'operation', value: 'list_groups' }, + }, + { + id: 'domain', + title: 'Domain', + type: 'short-input', + placeholder: 'Filter by domain (e.g., example.com)', + condition: { field: 'operation', value: 'list_groups' }, + }, + { + id: 'query', + title: 'Search Query', + type: 'short-input', + placeholder: 'Filter query (e.g., email:admin*)', + condition: { field: 'operation', value: 'list_groups' }, + }, + { + id: 'maxResults', + title: 'Max Results', + type: 'short-input', + placeholder: 'Maximum results (1-200)', + condition: { + field: 'operation', + value: ['list_groups', 'list_members'], + }, + }, + + { + id: 'groupKey', + title: 'Group Email or ID', + type: 'short-input', + placeholder: 'group@example.com or group ID', + required: true, + condition: { + field: 'operation', + value: [ + 'get_group', + 'update_group', + 'delete_group', + 'list_members', + 'get_member', + 'add_member', + 'update_member', + 'remove_member', + 'has_member', + ], + }, + }, + + { + id: 'email', + title: 'Group Email', + type: 'short-input', + placeholder: 'newgroup@example.com', + required: true, + condition: { field: 'operation', value: 'create_group' }, + }, + { + id: 'name', + title: 'Group Name', + type: 'short-input', + placeholder: 'Display name for the group', + required: true, + condition: { field: 'operation', value: 'create_group' }, + }, + { + id: 'description', + title: 'Description', + type: 'long-input', + placeholder: 'Optional description for the group', + condition: { field: 'operation', value: ['create_group', 'update_group'] }, + }, + + { + id: 'newName', + title: 'New Name', + type: 'short-input', + placeholder: 'New display name', + condition: { field: 'operation', value: 'update_group' }, + }, + { + id: 'newEmail', + title: 'New Email', + type: 'short-input', + placeholder: 'New email address', + condition: { field: 'operation', value: 'update_group' }, + }, + + { + id: 'memberKey', + title: 'Member Email or ID', + type: 'short-input', + placeholder: 'user@example.com or member ID', + required: true, + condition: { + field: 'operation', + value: ['get_member', 'update_member', 'remove_member', 'has_member'], + }, + }, + { + id: 'memberEmail', + title: 'Member Email', + type: 'short-input', + placeholder: 'user@example.com', + required: true, + condition: { field: 'operation', value: 'add_member' }, + }, + { + id: 'role', + title: 'Member Role', + type: 'dropdown', + options: [ + { id: 'MEMBER', label: 'Member' }, + { id: 'MANAGER', label: 'Manager' }, + { id: 'OWNER', label: 'Owner' }, + ], + condition: { field: 'operation', value: ['add_member', 'update_member'] }, + }, + { + id: 'roles', + title: 'Filter by Roles', + type: 'short-input', + placeholder: 'OWNER,MANAGER,MEMBER', + condition: { field: 'operation', value: 'list_members' }, + }, + ], + tools: { + access: [ + 'google_groups_list_groups', + 'google_groups_get_group', + 'google_groups_create_group', + 'google_groups_update_group', + 'google_groups_delete_group', + 'google_groups_list_members', + 'google_groups_get_member', + 'google_groups_add_member', + 'google_groups_remove_member', + 'google_groups_update_member', + 'google_groups_has_member', + ], + config: { + tool: (params) => { + switch (params.operation) { + case 'list_groups': + return 'google_groups_list_groups' + case 'get_group': + return 'google_groups_get_group' + case 'create_group': + return 'google_groups_create_group' + case 'update_group': + return 'google_groups_update_group' + case 'delete_group': + return 'google_groups_delete_group' + case 'list_members': + return 'google_groups_list_members' + case 'get_member': + return 'google_groups_get_member' + case 'add_member': + return 'google_groups_add_member' + case 'update_member': + return 'google_groups_update_member' + case 'remove_member': + return 'google_groups_remove_member' + case 'has_member': + return 'google_groups_has_member' + default: + throw new Error(`Invalid Google Groups operation: ${params.operation}`) + } + }, + params: (params) => { + const { credential, operation, ...rest } = params + + switch (operation) { + case 'list_groups': + return { + credential, + customer: rest.customer, + domain: rest.domain, + query: rest.query, + maxResults: rest.maxResults ? Number(rest.maxResults) : undefined, + } + case 'get_group': + case 'delete_group': + return { + credential, + groupKey: rest.groupKey, + } + case 'create_group': + return { + credential, + email: rest.email, + name: rest.name, + description: rest.description, + } + case 'update_group': + return { + credential, + groupKey: rest.groupKey, + name: rest.newName, + email: rest.newEmail, + description: rest.description, + } + case 'list_members': + return { + credential, + groupKey: rest.groupKey, + maxResults: rest.maxResults ? Number(rest.maxResults) : undefined, + roles: rest.roles, + } + case 'get_member': + case 'remove_member': + return { + credential, + groupKey: rest.groupKey, + memberKey: rest.memberKey, + } + case 'add_member': + return { + credential, + groupKey: rest.groupKey, + email: rest.memberEmail, + role: rest.role, + } + case 'update_member': + return { + credential, + groupKey: rest.groupKey, + memberKey: rest.memberKey, + role: rest.role, + } + case 'has_member': + return { + credential, + groupKey: rest.groupKey, + memberKey: rest.memberKey, + } + default: + return { credential, ...rest } + } + }, + }, + }, + inputs: { + operation: { type: 'string', description: 'Operation to perform' }, + credential: { type: 'string', description: 'Google Workspace OAuth credential' }, + customer: { type: 'string', description: 'Customer ID for listing groups' }, + domain: { type: 'string', description: 'Domain filter for listing groups' }, + query: { type: 'string', description: 'Search query for filtering groups' }, + maxResults: { type: 'number', description: 'Maximum results to return' }, + groupKey: { type: 'string', description: 'Group email address or ID' }, + email: { type: 'string', description: 'Email address for new group' }, + name: { type: 'string', description: 'Display name for group' }, + description: { type: 'string', description: 'Group description' }, + newName: { type: 'string', description: 'New display name for update' }, + newEmail: { type: 'string', description: 'New email for update' }, + memberKey: { type: 'string', description: 'Member email or ID' }, + memberEmail: { type: 'string', description: 'Email of member to add' }, + role: { type: 'string', description: 'Member role (MEMBER, MANAGER, OWNER)' }, + roles: { type: 'string', description: 'Filter by roles for list members' }, + }, + outputs: { + output: { type: 'json', description: 'Google Groups API response data' }, + }, +} diff --git a/apps/sim/blocks/registry.ts b/apps/sim/blocks/registry.ts index 40b5ad6aa..6062e8c40 100644 --- a/apps/sim/blocks/registry.ts +++ b/apps/sim/blocks/registry.ts @@ -34,6 +34,7 @@ import { GoogleCalendarBlock } from '@/blocks/blocks/google_calendar' import { GoogleDocsBlock } from '@/blocks/blocks/google_docs' import { GoogleDriveBlock } from '@/blocks/blocks/google_drive' import { GoogleFormsBlock } from '@/blocks/blocks/google_form' +import { GoogleGroupsBlock } from '@/blocks/blocks/google_groups' import { GoogleSheetsBlock } from '@/blocks/blocks/google_sheets' import { GoogleSlidesBlock } from '@/blocks/blocks/google_slides' import { GoogleVaultBlock } from '@/blocks/blocks/google_vault' @@ -175,6 +176,7 @@ export const registry: Record = { google_sheets: GoogleSheetsBlock, google_slides: GoogleSlidesBlock, google_vault: GoogleVaultBlock, + google_groups: GoogleGroupsBlock, hubspot: HubSpotBlock, huggingface: HuggingFaceBlock, human_in_the_loop: HumanInTheLoopBlock, diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx index d6dc0ae4d..972597cf8 100644 --- a/apps/sim/components/icons.tsx +++ b/apps/sim/components/icons.tsx @@ -4089,3 +4089,35 @@ export function PolymarketIcon(props: SVGProps) { ) } + +export function GoogleGroupsIcon(props: SVGProps) { + return ( + + + + + + + + ) +} diff --git a/apps/sim/lib/auth/auth.ts b/apps/sim/lib/auth/auth.ts index 934e76dc3..186912f46 100644 --- a/apps/sim/lib/auth/auth.ts +++ b/apps/sim/lib/auth/auth.ts @@ -498,6 +498,22 @@ export const auth = betterAuth({ redirectURI: `${getBaseUrl()}/api/auth/oauth2/callback/google-vault`, }, + { + providerId: 'google-groups', + 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/admin.directory.group', + 'https://www.googleapis.com/auth/admin.directory.group.member', + ], + prompt: 'consent', + redirectURI: `${getBaseUrl()}/api/auth/oauth2/callback/google-groups`, + }, + { providerId: 'microsoft-teams', clientId: env.MICROSOFT_CLIENT_ID as string, diff --git a/apps/sim/lib/oauth/oauth.ts b/apps/sim/lib/oauth/oauth.ts index bfd86227d..72badddea 100644 --- a/apps/sim/lib/oauth/oauth.ts +++ b/apps/sim/lib/oauth/oauth.ts @@ -11,6 +11,7 @@ import { GoogleDocsIcon, GoogleDriveIcon, GoogleFormsIcon, + GoogleGroupsIcon, GoogleIcon, GoogleSheetsIcon, HubspotIcon, @@ -80,6 +81,7 @@ export type OAuthService = | 'google-calendar' | 'google-vault' | 'google-forms' + | 'google-groups' | 'github' | 'x' // | 'supabase' @@ -224,6 +226,19 @@ export const OAUTH_PROVIDERS: Record = { ], scopeHints: ['ediscovery', 'devstorage'], }, + 'google-groups': { + id: 'google-groups', + name: 'Google Groups', + description: 'Manage Google Workspace Groups and their members.', + providerId: 'google-groups', + icon: (props) => GoogleGroupsIcon(props), + baseProviderIcon: (props) => GoogleIcon(props), + scopes: [ + 'https://www.googleapis.com/auth/admin.directory.group', + 'https://www.googleapis.com/auth/admin.directory.group.member', + ], + scopeHints: ['admin.directory.group'], + }, }, defaultService: 'gmail', }, diff --git a/apps/sim/tools/dynamodb/index.ts b/apps/sim/tools/dynamodb/index.ts index b2e7bdf46..69e95f790 100644 --- a/apps/sim/tools/dynamodb/index.ts +++ b/apps/sim/tools/dynamodb/index.ts @@ -5,4 +5,9 @@ import { queryTool } from './query' import { scanTool } from './scan' import { updateTool } from './update' -export { deleteTool, getTool, putTool, queryTool, scanTool, updateTool } +export const dynamodbDeleteTool = deleteTool +export const dynamodbGetTool = getTool +export const dynamodbPutTool = putTool +export const dynamodbQueryTool = queryTool +export const dynamodbScanTool = scanTool +export const dynamodbUpdateTool = updateTool diff --git a/apps/sim/tools/firecrawl/index.ts b/apps/sim/tools/firecrawl/index.ts index 870b1e820..ae0ca66e4 100644 --- a/apps/sim/tools/firecrawl/index.ts +++ b/apps/sim/tools/firecrawl/index.ts @@ -4,4 +4,8 @@ import { mapTool } from '@/tools/firecrawl/map' import { scrapeTool } from '@/tools/firecrawl/scrape' import { searchTool } from '@/tools/firecrawl/search' -export { scrapeTool, searchTool, crawlTool, mapTool, extractTool } +export const firecrawlScrapeTool = scrapeTool +export const firecrawlSearchTool = searchTool +export const firecrawlCrawlTool = crawlTool +export const firecrawlMapTool = mapTool +export const firecrawlExtractTool = extractTool diff --git a/apps/sim/tools/google/index.ts b/apps/sim/tools/google/index.ts index baa2a16f5..4419837d9 100644 --- a/apps/sim/tools/google/index.ts +++ b/apps/sim/tools/google/index.ts @@ -1,3 +1,3 @@ -import { searchTool } from '@/tools/google/search' +import { searchTool } from './search' -export { searchTool } +export const googleSearchTool = searchTool diff --git a/apps/sim/tools/google_groups/add_member.ts b/apps/sim/tools/google_groups/add_member.ts new file mode 100644 index 000000000..0a306efb6 --- /dev/null +++ b/apps/sim/tools/google_groups/add_member.ts @@ -0,0 +1,72 @@ +import type { ToolConfig } from '@/tools/types' +import type { GoogleGroupsAddMemberParams, GoogleGroupsResponse } from './types' + +export const addMemberTool: ToolConfig = { + id: 'google_groups_add_member', + name: 'Google Groups Add Member', + description: 'Add a new member to a Google Group', + version: '1.0.0', + + oauth: { + required: true, + provider: 'google-groups', + }, + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + groupKey: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Group email address or unique group ID', + }, + email: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Email address of the member to add', + }, + role: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Role for the member (MEMBER, MANAGER, or OWNER). Defaults to MEMBER.', + }, + }, + + request: { + url: (params) => { + const encodedGroupKey = encodeURIComponent(params.groupKey) + return `https://admin.googleapis.com/admin/directory/v1/groups/${encodedGroupKey}/members` + }, + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: Record = { + email: params.email, + role: params.role || 'MEMBER', + } + + return JSON.stringify(body) + }, + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.error?.message || 'Failed to add member to group') + } + return { + success: true, + output: data, + } + }, +} diff --git a/apps/sim/tools/google_groups/create_group.ts b/apps/sim/tools/google_groups/create_group.ts new file mode 100644 index 000000000..fb696bed6 --- /dev/null +++ b/apps/sim/tools/google_groups/create_group.ts @@ -0,0 +1,73 @@ +import type { ToolConfig } from '@/tools/types' +import type { GoogleGroupsCreateParams, GoogleGroupsResponse } from './types' + +export const createGroupTool: ToolConfig = { + id: 'google_groups_create_group', + name: 'Google Groups Create Group', + description: 'Create a new Google Group in the domain', + version: '1.0.0', + + oauth: { + required: true, + provider: 'google-groups', + }, + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + email: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Email address for the new group (e.g., team@yourdomain.com)', + }, + name: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Display name for the group', + }, + description: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Description of the group', + }, + }, + + request: { + url: () => 'https://admin.googleapis.com/admin/directory/v1/groups', + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: Record = { + email: params.email, + name: params.name, + } + + if (params.description) { + body.description = params.description + } + + return JSON.stringify(body) + }, + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.error?.message || 'Failed to create group') + } + return { + success: true, + output: data, + } + }, +} diff --git a/apps/sim/tools/google_groups/delete_group.ts b/apps/sim/tools/google_groups/delete_group.ts new file mode 100644 index 000000000..93eb17f21 --- /dev/null +++ b/apps/sim/tools/google_groups/delete_group.ts @@ -0,0 +1,52 @@ +import type { ToolConfig } from '@/tools/types' +import type { GoogleGroupsDeleteParams, GoogleGroupsResponse } from './types' + +export const deleteGroupTool: ToolConfig = { + id: 'google_groups_delete_group', + name: 'Google Groups Delete Group', + description: 'Delete a Google Group', + version: '1.0.0', + + oauth: { + required: true, + provider: 'google-groups', + }, + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + groupKey: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Group email address or unique group ID to delete', + }, + }, + + request: { + url: (params) => { + const encodedGroupKey = encodeURIComponent(params.groupKey) + return `https://admin.googleapis.com/admin/directory/v1/groups/${encodedGroupKey}` + }, + method: 'DELETE', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response) => { + if (!response.ok) { + const data = await response.json() + throw new Error(data.error?.message || 'Failed to delete group') + } + return { + success: true, + output: { message: 'Group deleted successfully' }, + } + }, +} diff --git a/apps/sim/tools/google_groups/get_group.ts b/apps/sim/tools/google_groups/get_group.ts new file mode 100644 index 000000000..bc911a49f --- /dev/null +++ b/apps/sim/tools/google_groups/get_group.ts @@ -0,0 +1,52 @@ +import type { ToolConfig } from '@/tools/types' +import type { GoogleGroupsGetParams, GoogleGroupsResponse } from './types' + +export const getGroupTool: ToolConfig = { + id: 'google_groups_get_group', + name: 'Google Groups Get Group', + description: 'Get details of a specific Google Group by email or group ID', + version: '1.0.0', + + oauth: { + required: true, + provider: 'google-groups', + }, + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + groupKey: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Group email address or unique group ID', + }, + }, + + request: { + url: (params) => { + const encodedGroupKey = encodeURIComponent(params.groupKey) + return `https://admin.googleapis.com/admin/directory/v1/groups/${encodedGroupKey}` + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.error?.message || 'Failed to get group') + } + return { + success: true, + output: data, + } + }, +} diff --git a/apps/sim/tools/google_groups/get_member.ts b/apps/sim/tools/google_groups/get_member.ts new file mode 100644 index 000000000..cc46097b9 --- /dev/null +++ b/apps/sim/tools/google_groups/get_member.ts @@ -0,0 +1,59 @@ +import type { ToolConfig } from '@/tools/types' +import type { GoogleGroupsGetMemberParams, GoogleGroupsResponse } from './types' + +export const getMemberTool: ToolConfig = { + id: 'google_groups_get_member', + name: 'Google Groups Get Member', + description: 'Get details of a specific member in a Google Group', + version: '1.0.0', + + oauth: { + required: true, + provider: 'google-groups', + }, + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + groupKey: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Group email address or unique group ID', + }, + memberKey: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Member email address or unique member ID', + }, + }, + + request: { + url: (params) => { + const encodedGroupKey = encodeURIComponent(params.groupKey) + const encodedMemberKey = encodeURIComponent(params.memberKey) + return `https://admin.googleapis.com/admin/directory/v1/groups/${encodedGroupKey}/members/${encodedMemberKey}` + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.error?.message || 'Failed to get group member') + } + return { + success: true, + output: data, + } + }, +} diff --git a/apps/sim/tools/google_groups/has_member.ts b/apps/sim/tools/google_groups/has_member.ts new file mode 100644 index 000000000..8e1d838c2 --- /dev/null +++ b/apps/sim/tools/google_groups/has_member.ts @@ -0,0 +1,59 @@ +import type { ToolConfig } from '@/tools/types' +import type { GoogleGroupsHasMemberParams, GoogleGroupsResponse } from './types' + +export const hasMemberTool: ToolConfig = { + id: 'google_groups_has_member', + name: 'Google Groups Has Member', + description: 'Check if a user is a member of a Google Group', + version: '1.0.0', + + oauth: { + required: true, + provider: 'google-groups', + }, + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + groupKey: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Group email address or unique group ID', + }, + memberKey: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Member email address or unique member ID to check', + }, + }, + + request: { + url: (params) => { + const encodedGroupKey = encodeURIComponent(params.groupKey) + const encodedMemberKey = encodeURIComponent(params.memberKey) + return `https://admin.googleapis.com/admin/directory/v1/groups/${encodedGroupKey}/hasMember/${encodedMemberKey}` + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.error?.message || 'Failed to check membership') + } + return { + success: true, + output: data, + } + }, +} diff --git a/apps/sim/tools/google_groups/index.ts b/apps/sim/tools/google_groups/index.ts new file mode 100644 index 000000000..c1f7aa42c --- /dev/null +++ b/apps/sim/tools/google_groups/index.ts @@ -0,0 +1,23 @@ +import { addMemberTool } from './add_member' +import { createGroupTool } from './create_group' +import { deleteGroupTool } from './delete_group' +import { getGroupTool } from './get_group' +import { getMemberTool } from './get_member' +import { hasMemberTool } from './has_member' +import { listGroupsTool } from './list_groups' +import { listMembersTool } from './list_members' +import { removeMemberTool } from './remove_member' +import { updateGroupTool } from './update_group' +import { updateMemberTool } from './update_member' + +export const googleGroupsAddMemberTool = addMemberTool +export const googleGroupsCreateGroupTool = createGroupTool +export const googleGroupsDeleteGroupTool = deleteGroupTool +export const googleGroupsGetGroupTool = getGroupTool +export const googleGroupsGetMemberTool = getMemberTool +export const googleGroupsHasMemberTool = hasMemberTool +export const googleGroupsListGroupsTool = listGroupsTool +export const googleGroupsListMembersTool = listMembersTool +export const googleGroupsRemoveMemberTool = removeMemberTool +export const googleGroupsUpdateGroupTool = updateGroupTool +export const googleGroupsUpdateMemberTool = updateMemberTool diff --git a/apps/sim/tools/google_groups/list_groups.ts b/apps/sim/tools/google_groups/list_groups.ts new file mode 100644 index 000000000..0a2531bf0 --- /dev/null +++ b/apps/sim/tools/google_groups/list_groups.ts @@ -0,0 +1,97 @@ +import type { ToolConfig } from '@/tools/types' +import type { GoogleGroupsListParams, GoogleGroupsResponse } from './types' + +export const listGroupsTool: ToolConfig = { + id: 'google_groups_list_groups', + name: 'Google Groups List Groups', + description: 'List all groups in a Google Workspace domain', + version: '1.0.0', + + oauth: { + required: true, + provider: 'google-groups', + }, + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + customer: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Customer ID or "my_customer" for the authenticated user\'s domain', + }, + domain: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Domain name to filter groups by', + }, + maxResults: { + type: 'number', + required: false, + visibility: 'user-only', + description: 'Maximum number of results to return (1-200)', + }, + pageToken: { + type: 'string', + required: false, + visibility: 'hidden', + description: 'Token for pagination', + }, + query: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Search query to filter groups (e.g., "email:admin*")', + }, + }, + + request: { + url: (params) => { + const url = new URL('https://admin.googleapis.com/admin/directory/v1/groups') + + // Use my_customer as default if no customer or domain specified + if (params.customer) { + url.searchParams.set('customer', params.customer) + } else if (!params.domain) { + url.searchParams.set('customer', 'my_customer') + } + + if (params.domain) { + url.searchParams.set('domain', params.domain) + } + if (params.maxResults) { + url.searchParams.set('maxResults', String(params.maxResults)) + } + if (params.pageToken) { + url.searchParams.set('pageToken', params.pageToken) + } + if (params.query) { + url.searchParams.set('query', params.query) + } + + return url.toString() + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.error?.message || 'Failed to list groups') + } + return { + success: true, + output: data, + } + }, +} diff --git a/apps/sim/tools/google_groups/list_members.ts b/apps/sim/tools/google_groups/list_members.ts new file mode 100644 index 000000000..dd3dcda9a --- /dev/null +++ b/apps/sim/tools/google_groups/list_members.ts @@ -0,0 +1,84 @@ +import type { ToolConfig } from '@/tools/types' +import type { GoogleGroupsListMembersParams, GoogleGroupsResponse } from './types' + +export const listMembersTool: ToolConfig = { + id: 'google_groups_list_members', + name: 'Google Groups List Members', + description: 'List all members of a Google Group', + version: '1.0.0', + + oauth: { + required: true, + provider: 'google-groups', + }, + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + groupKey: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Group email address or unique group ID', + }, + maxResults: { + type: 'number', + required: false, + visibility: 'user-only', + description: 'Maximum number of results to return (1-200)', + }, + pageToken: { + type: 'string', + required: false, + visibility: 'hidden', + description: 'Token for pagination', + }, + roles: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter by roles (comma-separated: OWNER, MANAGER, MEMBER)', + }, + }, + + request: { + url: (params) => { + const encodedGroupKey = encodeURIComponent(params.groupKey) + const url = new URL( + `https://admin.googleapis.com/admin/directory/v1/groups/${encodedGroupKey}/members` + ) + + if (params.maxResults) { + url.searchParams.set('maxResults', String(params.maxResults)) + } + if (params.pageToken) { + url.searchParams.set('pageToken', params.pageToken) + } + if (params.roles) { + url.searchParams.set('roles', params.roles) + } + + return url.toString() + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.error?.message || 'Failed to list group members') + } + return { + success: true, + output: data, + } + }, +} diff --git a/apps/sim/tools/google_groups/remove_member.ts b/apps/sim/tools/google_groups/remove_member.ts new file mode 100644 index 000000000..5c15fdb98 --- /dev/null +++ b/apps/sim/tools/google_groups/remove_member.ts @@ -0,0 +1,59 @@ +import type { ToolConfig } from '@/tools/types' +import type { GoogleGroupsRemoveMemberParams, GoogleGroupsResponse } from './types' + +export const removeMemberTool: ToolConfig = { + id: 'google_groups_remove_member', + name: 'Google Groups Remove Member', + description: 'Remove a member from a Google Group', + version: '1.0.0', + + oauth: { + required: true, + provider: 'google-groups', + }, + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + groupKey: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Group email address or unique group ID', + }, + memberKey: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Email address or unique ID of the member to remove', + }, + }, + + request: { + url: (params) => { + const encodedGroupKey = encodeURIComponent(params.groupKey) + const encodedMemberKey = encodeURIComponent(params.memberKey) + return `https://admin.googleapis.com/admin/directory/v1/groups/${encodedGroupKey}/members/${encodedMemberKey}` + }, + method: 'DELETE', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response) => { + if (!response.ok) { + const data = await response.json() + throw new Error(data.error?.message || 'Failed to remove member from group') + } + return { + success: true, + output: { message: 'Member removed successfully' }, + } + }, +} diff --git a/apps/sim/tools/google_groups/types.ts b/apps/sim/tools/google_groups/types.ts new file mode 100644 index 000000000..5de0f4d74 --- /dev/null +++ b/apps/sim/tools/google_groups/types.ts @@ -0,0 +1,111 @@ +import type { ToolResponse } from '@/tools/types' + +/** + * Common parameters for Google Groups API calls + */ +export interface GoogleGroupsCommonParams { + accessToken: string +} + +/** + * Parameters for listing groups + */ +export interface GoogleGroupsListParams extends GoogleGroupsCommonParams { + customer?: string + domain?: string + maxResults?: number + pageToken?: string + query?: string +} + +/** + * Parameters for getting a specific group + */ +export interface GoogleGroupsGetParams extends GoogleGroupsCommonParams { + groupKey: string +} + +/** + * Parameters for creating a group + */ +export interface GoogleGroupsCreateParams extends GoogleGroupsCommonParams { + email: string + name: string + description?: string +} + +/** + * Parameters for updating a group + */ +export interface GoogleGroupsUpdateParams extends GoogleGroupsCommonParams { + groupKey: string + name?: string + description?: string + email?: string +} + +/** + * Parameters for deleting a group + */ +export interface GoogleGroupsDeleteParams extends GoogleGroupsCommonParams { + groupKey: string +} + +/** + * Parameters for listing group members + */ +export interface GoogleGroupsListMembersParams extends GoogleGroupsCommonParams { + groupKey: string + maxResults?: number + pageToken?: string + roles?: string +} + +/** + * Parameters for getting a specific member + */ +export interface GoogleGroupsGetMemberParams extends GoogleGroupsCommonParams { + groupKey: string + memberKey: string +} + +/** + * Parameters for adding a member to a group + */ +export interface GoogleGroupsAddMemberParams extends GoogleGroupsCommonParams { + groupKey: string + email: string + role?: 'MEMBER' | 'MANAGER' | 'OWNER' +} + +/** + * Parameters for removing a member from a group + */ +export interface GoogleGroupsRemoveMemberParams extends GoogleGroupsCommonParams { + groupKey: string + memberKey: string +} + +/** + * Parameters for updating a member's role in a group + */ +export interface GoogleGroupsUpdateMemberParams extends GoogleGroupsCommonParams { + groupKey: string + memberKey: string + role: 'MEMBER' | 'MANAGER' | 'OWNER' +} + +/** + * Parameters for checking if a user is a member of a group + */ +export interface GoogleGroupsHasMemberParams extends GoogleGroupsCommonParams { + groupKey: string + memberKey: string +} + +/** + * Standard response for Google Groups operations + */ +export interface GoogleGroupsResponse extends ToolResponse { + output: Record +} diff --git a/apps/sim/tools/google_groups/update_group.ts b/apps/sim/tools/google_groups/update_group.ts new file mode 100644 index 000000000..1fef8e34c --- /dev/null +++ b/apps/sim/tools/google_groups/update_group.ts @@ -0,0 +1,85 @@ +import type { ToolConfig } from '@/tools/types' +import type { GoogleGroupsResponse, GoogleGroupsUpdateParams } from './types' + +export const updateGroupTool: ToolConfig = { + id: 'google_groups_update_group', + name: 'Google Groups Update Group', + description: 'Update an existing Google Group', + version: '1.0.0', + + oauth: { + required: true, + provider: 'google-groups', + }, + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + groupKey: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Group email address or unique group ID', + }, + name: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'New display name for the group', + }, + description: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'New description for the group', + }, + email: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'New email address for the group', + }, + }, + + request: { + url: (params) => { + const encodedGroupKey = encodeURIComponent(params.groupKey) + return `https://admin.googleapis.com/admin/directory/v1/groups/${encodedGroupKey}` + }, + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: Record = {} + + if (params.name) { + body.name = params.name + } + if (params.description) { + body.description = params.description + } + if (params.email) { + body.email = params.email + } + + return JSON.stringify(body) + }, + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.error?.message || 'Failed to update group') + } + return { + success: true, + output: data, + } + }, +} diff --git a/apps/sim/tools/google_groups/update_member.ts b/apps/sim/tools/google_groups/update_member.ts new file mode 100644 index 000000000..d1193dec2 --- /dev/null +++ b/apps/sim/tools/google_groups/update_member.ts @@ -0,0 +1,70 @@ +import type { ToolConfig } from '@/tools/types' +import type { GoogleGroupsResponse, GoogleGroupsUpdateMemberParams } from './types' + +export const updateMemberTool: ToolConfig = { + id: 'google_groups_update_member', + name: 'Google Groups Update Member', + description: "Update a member's role in a Google Group (promote or demote)", + version: '1.0.0', + + oauth: { + required: true, + provider: 'google-groups', + }, + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + groupKey: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Group email address or unique group ID', + }, + memberKey: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Member email address or unique member ID', + }, + role: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'New role for the member (MEMBER, MANAGER, or OWNER)', + }, + }, + + request: { + url: (params) => { + const encodedGroupKey = encodeURIComponent(params.groupKey) + const encodedMemberKey = encodeURIComponent(params.memberKey) + return `https://admin.googleapis.com/admin/directory/v1/groups/${encodedGroupKey}/members/${encodedMemberKey}` + }, + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + return JSON.stringify({ + role: params.role, + }) + }, + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.error?.message || 'Failed to update member role') + } + return { + success: true, + output: data, + } + }, +} diff --git a/apps/sim/tools/http/index.ts b/apps/sim/tools/http/index.ts index aa6045137..7eb208188 100644 --- a/apps/sim/tools/http/index.ts +++ b/apps/sim/tools/http/index.ts @@ -1,3 +1,3 @@ -import { requestTool } from '@/tools/http/request' +import { requestTool } from './request' -export { requestTool } +export const httpRequestTool = requestTool diff --git a/apps/sim/tools/jina/index.ts b/apps/sim/tools/jina/index.ts index b5c18e8e0..07620f379 100644 --- a/apps/sim/tools/jina/index.ts +++ b/apps/sim/tools/jina/index.ts @@ -1,4 +1,5 @@ -import { readUrlTool } from '@/tools/jina/read_url' -import { searchTool } from '@/tools/jina/search' +import { readUrlTool } from './read_url' +import { searchTool } from './search' -export { readUrlTool, searchTool } +export const jinaReadUrlTool = readUrlTool +export const jinaSearchTool = searchTool diff --git a/apps/sim/tools/mongodb/index.ts b/apps/sim/tools/mongodb/index.ts index b532035cb..8407b7772 100644 --- a/apps/sim/tools/mongodb/index.ts +++ b/apps/sim/tools/mongodb/index.ts @@ -1,6 +1,13 @@ -export { deleteTool } from './delete' -export { executeTool } from './execute' -export { insertTool } from './insert' -export { queryTool } from './query' +import { deleteTool } from './delete' +import { executeTool } from './execute' +import { insertTool } from './insert' +import { queryTool } from './query' +import { updateTool } from './update' + +export const mongodbDeleteTool = deleteTool +export const mongodbExecuteTool = executeTool +export const mongodbInsertTool = insertTool +export const mongodbQueryTool = queryTool +export const mongodbUpdateTool = updateTool + export * from './types' -export { updateTool } from './update' diff --git a/apps/sim/tools/mysql/index.ts b/apps/sim/tools/mysql/index.ts index b532035cb..1e388a67e 100644 --- a/apps/sim/tools/mysql/index.ts +++ b/apps/sim/tools/mysql/index.ts @@ -1,6 +1,13 @@ -export { deleteTool } from './delete' -export { executeTool } from './execute' -export { insertTool } from './insert' -export { queryTool } from './query' +import { deleteTool } from './delete' +import { executeTool } from './execute' +import { insertTool } from './insert' +import { queryTool } from './query' +import { updateTool } from './update' + +export const mysqlDeleteTool = deleteTool +export const mysqlExecuteTool = executeTool +export const mysqlInsertTool = insertTool +export const mysqlQueryTool = queryTool +export const mysqlUpdateTool = updateTool + export * from './types' -export { updateTool } from './update' diff --git a/apps/sim/tools/neo4j/index.ts b/apps/sim/tools/neo4j/index.ts index 1e4ac3704..ba5c38830 100644 --- a/apps/sim/tools/neo4j/index.ts +++ b/apps/sim/tools/neo4j/index.ts @@ -1,7 +1,14 @@ -export { createTool } from './create' -export { deleteTool } from './delete' -export { executeTool } from './execute' -export { mergeTool } from './merge' -export { queryTool } from './query' +import { createTool } from './create' +import { deleteTool } from './delete' +import { executeTool } from './execute' +import { mergeTool } from './merge' +import { queryTool } from './query' +import { updateTool } from './update' + +export const neo4jCreateTool = createTool +export const neo4jDeleteTool = deleteTool +export const neo4jExecuteTool = executeTool +export const neo4jMergeTool = mergeTool +export const neo4jQueryTool = queryTool +export const neo4jUpdateTool = updateTool export * from './types' -export { updateTool } from './update' diff --git a/apps/sim/tools/openai/index.ts b/apps/sim/tools/openai/index.ts index 63d875c1f..137ea17f1 100644 --- a/apps/sim/tools/openai/index.ts +++ b/apps/sim/tools/openai/index.ts @@ -1,4 +1,5 @@ import { embeddingsTool } from '@/tools/openai/embeddings' import { imageTool } from '@/tools/openai/image' -export { embeddingsTool, imageTool } +export const openAIEmbeddingsTool = embeddingsTool +export const openAIImageTool = imageTool diff --git a/apps/sim/tools/postgresql/index.ts b/apps/sim/tools/postgresql/index.ts index 651026e93..ea54708a8 100644 --- a/apps/sim/tools/postgresql/index.ts +++ b/apps/sim/tools/postgresql/index.ts @@ -4,4 +4,8 @@ import { insertTool } from './insert' import { queryTool } from './query' import { updateTool } from './update' -export { deleteTool, executeTool, insertTool, queryTool, updateTool } +export const postgresDeleteTool = deleteTool +export const postgresExecuteTool = executeTool +export const postgresInsertTool = insertTool +export const postgresQueryTool = queryTool +export const postgresUpdateTool = updateTool diff --git a/apps/sim/tools/rds/index.ts b/apps/sim/tools/rds/index.ts index 651026e93..49bf77377 100644 --- a/apps/sim/tools/rds/index.ts +++ b/apps/sim/tools/rds/index.ts @@ -4,4 +4,8 @@ import { insertTool } from './insert' import { queryTool } from './query' import { updateTool } from './update' -export { deleteTool, executeTool, insertTool, queryTool, updateTool } +export const rdsDeleteTool = deleteTool +export const rdsExecuteTool = executeTool +export const rdsInsertTool = insertTool +export const rdsQueryTool = queryTool +export const rdsUpdateTool = updateTool diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index 2433ae86d..7d2d73738 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -145,12 +145,12 @@ import { dropboxUploadTool, } from '@/tools/dropbox' import { - deleteTool as dynamodbDeleteTool, - getTool as dynamodbGetTool, - putTool as dynamodbPutTool, - queryTool as dynamodbQueryTool, - scanTool as dynamodbScanTool, - updateTool as dynamodbUpdateTool, + dynamodbDeleteTool, + dynamodbGetTool, + dynamodbPutTool, + dynamodbQueryTool, + dynamodbScanTool, + dynamodbUpdateTool, } from '@/tools/dynamodb' import { elasticsearchBulkTool, @@ -176,11 +176,11 @@ import { } from '@/tools/exa' import { fileParseTool } from '@/tools/file' import { - crawlTool, - extractTool, - searchTool as firecrawlSearchTool, - mapTool, - scrapeTool, + firecrawlCrawlTool, + firecrawlExtractTool, + firecrawlMapTool, + firecrawlScrapeTool, + firecrawlSearchTool, } from '@/tools/firecrawl' import { functionExecuteTool } from '@/tools/function' import { @@ -272,7 +272,7 @@ import { gmailSendTool, gmailUnarchiveTool, } from '@/tools/gmail' -import { searchTool as googleSearchTool } from '@/tools/google' +import { googleSearchTool } from '@/tools/google' import { googleCalendarCreateTool, googleCalendarGetTool, @@ -289,6 +289,19 @@ import { googleDriveUploadTool, } from '@/tools/google_drive' import { googleFormsGetResponsesTool } from '@/tools/google_form' +import { + googleGroupsAddMemberTool, + googleGroupsCreateGroupTool, + googleGroupsDeleteGroupTool, + googleGroupsGetGroupTool, + googleGroupsGetMemberTool, + googleGroupsHasMemberTool, + googleGroupsListGroupsTool, + googleGroupsListMembersTool, + googleGroupsRemoveMemberTool, + googleGroupsUpdateGroupTool, + googleGroupsUpdateMemberTool, +} from '@/tools/google_groups' import { googleSheetsAppendTool, googleSheetsReadTool, @@ -335,7 +348,7 @@ import { grafanaUpdateDashboardTool, } from '@/tools/grafana' import { guardrailsValidateTool } from '@/tools/guardrails' -import { requestTool as httpRequest } from '@/tools/http' +import { httpRequestTool } from '@/tools/http' import { hubspotCreateCompanyTool, hubspotCreateContactTool, @@ -424,7 +437,7 @@ import { intercomSearchConversationsTool, intercomUpdateContactTool, } from '@/tools/intercom' -import { searchTool as jinaSearchTool, readUrlTool } from '@/tools/jina' +import { jinaReadUrlTool, jinaSearchTool } from '@/tools/jina' import { jiraAddCommentTool, jiraAddWatcherTool, @@ -682,26 +695,26 @@ import { } from '@/tools/microsoft_teams' import { mistralParserTool } from '@/tools/mistral' import { - deleteTool as mongodbDeleteTool, - executeTool as mongodbExecuteTool, - insertTool as mongodbInsertTool, - queryTool as mongodbQueryTool, - updateTool as mongodbUpdateTool, + mongodbDeleteTool, + mongodbExecuteTool, + mongodbInsertTool, + mongodbQueryTool, + mongodbUpdateTool, } from '@/tools/mongodb' import { - deleteTool as mysqlDeleteTool, - executeTool as mysqlExecuteTool, - insertTool as mysqlInsertTool, - queryTool as mysqlQueryTool, - updateTool as mysqlUpdateTool, + mysqlDeleteTool, + mysqlExecuteTool, + mysqlInsertTool, + mysqlQueryTool, + mysqlUpdateTool, } from '@/tools/mysql' import { - createTool as neo4jCreateTool, - deleteTool as neo4jDeleteTool, - executeTool as neo4jExecuteTool, - mergeTool as neo4jMergeTool, - queryTool as neo4jQueryTool, - updateTool as neo4jUpdateTool, + neo4jCreateTool, + neo4jDeleteTool, + neo4jExecuteTool, + neo4jMergeTool, + neo4jQueryTool, + neo4jUpdateTool, } from '@/tools/neo4j' import { notionCreateDatabaseTool, @@ -719,7 +732,7 @@ import { onedriveListTool, onedriveUploadTool, } from '@/tools/onedrive' -import { imageTool, embeddingsTool as openAIEmbeddings } from '@/tools/openai' +import { openAIEmbeddingsTool, openAIImageTool } from '@/tools/openai' import { outlookCopyTool, outlookDeleteTool, @@ -780,11 +793,11 @@ import { polymarketSearchTool, } from '@/tools/polymarket' import { - deleteTool as postgresDeleteTool, - executeTool as postgresExecuteTool, - insertTool as postgresInsertTool, - queryTool as postgresQueryTool, - updateTool as postgresUpdateTool, + postgresDeleteTool, + postgresExecuteTool, + postgresInsertTool, + postgresQueryTool, + postgresUpdateTool, } from '@/tools/postgresql' import { posthogBatchEventsTool, @@ -873,11 +886,11 @@ import { } from '@/tools/pylon' import { qdrantFetchTool, qdrantSearchTool, qdrantUpsertTool } from '@/tools/qdrant' import { - deleteTool as rdsDeleteTool, - executeTool as rdsExecuteTool, - insertTool as rdsInsertTool, - queryTool as rdsQueryTool, - updateTool as rdsUpdateTool, + rdsDeleteTool, + rdsExecuteTool, + rdsInsertTool, + rdsQueryTool, + rdsUpdateTool, } from '@/tools/rds' import { redditDeleteTool, @@ -961,7 +974,7 @@ import { updateIssueTool, updateProjectTool, } from '@/tools/sentry' -import { searchTool as serperSearch } from '@/tools/serper' +import { serperSearchTool } from '@/tools/serper' import { sharepointAddListItemTool, sharepointCreateListTool, @@ -1011,19 +1024,19 @@ import { import { smsSendTool } from '@/tools/sms' import { smtpSendMailTool } from '@/tools/smtp' import { - checkCommandExistsTool as sshCheckCommandExistsTool, - checkFileExistsTool as sshCheckFileExistsTool, - createDirectoryTool as sshCreateDirectoryTool, - deleteFileTool as sshDeleteFileTool, - downloadFileTool as sshDownloadFileTool, - executeCommandTool as sshExecuteCommandTool, - executeScriptTool as sshExecuteScriptTool, - getSystemInfoTool as sshGetSystemInfoTool, - listDirectoryTool as sshListDirectoryTool, - moveRenameTool as sshMoveRenameTool, - readFileContentTool as sshReadFileContentTool, - uploadFileTool as sshUploadFileTool, - writeFileContentTool as sshWriteFileContentTool, + sshCheckCommandExistsTool, + sshCheckFileExistsTool, + sshCreateDirectoryTool, + sshDeleteFileTool, + sshDownloadFileTool, + sshExecuteCommandTool, + sshExecuteScriptTool, + sshGetSystemInfoTool, + sshListDirectoryTool, + sshMoveRenameTool, + sshReadFileContentTool, + sshUploadFileTool, + sshWriteFileContentTool, } from '@/tools/ssh' import { stagehandAgentTool, stagehandExtractTool } from '@/tools/stagehand' import { @@ -1283,17 +1296,17 @@ export const tools: Record = { asana_search_tasks: asanaSearchTasksTool, asana_add_comment: asanaAddCommentTool, browser_use_run_task: browserUseRunTaskTool, - openai_embeddings: openAIEmbeddings, - http_request: httpRequest, + openai_embeddings: openAIEmbeddingsTool, + http_request: httpRequestTool, huggingface_chat: huggingfaceChatTool, function_execute: functionExecuteTool, vision_tool: visionTool, file_parser: fileParseTool, - firecrawl_scrape: scrapeTool, + firecrawl_scrape: firecrawlScrapeTool, firecrawl_search: firecrawlSearchTool, - firecrawl_crawl: crawlTool, - firecrawl_map: mapTool, - firecrawl_extract: extractTool, + firecrawl_crawl: firecrawlCrawlTool, + firecrawl_map: firecrawlMapTool, + firecrawl_extract: firecrawlExtractTool, grafana_get_dashboard: grafanaGetDashboardTool, grafana_list_dashboards: grafanaListDashboardsTool, grafana_create_dashboard: grafanaCreateDashboardTool, @@ -1315,7 +1328,7 @@ export const tools: Record = { grafana_create_folder: grafanaCreateFolderTool, google_search: googleSearchTool, guardrails_validate: guardrailsValidateTool, - jina_read_url: readUrlTool, + jina_read_url: jinaReadUrlTool, jina_search: jinaSearchTool, linkup_search: linkupSearchTool, linkedin_share_post: linkedInSharePostTool, @@ -1429,7 +1442,7 @@ export const tools: Record = { slack_add_reaction: slackAddReactionTool, github_repo_info: githubRepoInfoTool, github_latest_commit: githubLatestCommitTool, - serper_search: serperSearch, + serper_search: serperSearchTool, tavily_search: tavilySearchTool, tavily_extract: tavilyExtractTool, tavily_crawl: tavilyCrawlTool, @@ -1914,7 +1927,7 @@ export const tools: Record = { datadog_create_downtime: datadogCreateDowntimeTool, datadog_list_downtimes: datadogListDowntimesTool, datadog_cancel_downtime: datadogCancelDowntimeTool, - openai_image: imageTool, + openai_image: openAIImageTool, microsoft_teams_read_chat: microsoftTeamsReadChatTool, microsoft_teams_write_chat: microsoftTeamsWriteChatTool, microsoft_teams_read_channel: microsoftTeamsReadChannelTool, @@ -2115,6 +2128,17 @@ export const tools: Record = { google_vault_create_matters: createMattersTool, google_vault_list_matters: listMattersTool, google_vault_download_export_file: downloadExportFileTool, + google_groups_list_groups: googleGroupsListGroupsTool, + google_groups_get_group: googleGroupsGetGroupTool, + google_groups_create_group: googleGroupsCreateGroupTool, + google_groups_update_group: googleGroupsUpdateGroupTool, + google_groups_delete_group: googleGroupsDeleteGroupTool, + google_groups_list_members: googleGroupsListMembersTool, + google_groups_get_member: googleGroupsGetMemberTool, + google_groups_add_member: googleGroupsAddMemberTool, + google_groups_remove_member: googleGroupsRemoveMemberTool, + google_groups_update_member: googleGroupsUpdateMemberTool, + google_groups_has_member: googleGroupsHasMemberTool, qdrant_fetch_points: qdrantFetchTool, qdrant_search_vector: qdrantSearchTool, qdrant_upsert_points: qdrantUpsertTool, diff --git a/apps/sim/tools/serper/index.ts b/apps/sim/tools/serper/index.ts index e8a347307..d6b405b2a 100644 --- a/apps/sim/tools/serper/index.ts +++ b/apps/sim/tools/serper/index.ts @@ -1,3 +1,3 @@ -import { searchTool } from '@/tools/serper/search' +import { searchTool } from './search' -export { searchTool } +export const serperSearchTool = searchTool diff --git a/apps/sim/tools/ssh/index.ts b/apps/sim/tools/ssh/index.ts index be79a5e0e..4c33182f8 100644 --- a/apps/sim/tools/ssh/index.ts +++ b/apps/sim/tools/ssh/index.ts @@ -1,14 +1,27 @@ -export { checkCommandExistsTool } from './check_command_exists' -export { checkFileExistsTool } from './check_file_exists' -export { createDirectoryTool } from './create_directory' -export { deleteFileTool } from './delete_file' -export { downloadFileTool } from './download_file' -export { executeCommandTool } from './execute_command' -export { executeScriptTool } from './execute_script' -export { getSystemInfoTool } from './get_system_info' -export { listDirectoryTool } from './list_directory' -export { moveRenameTool } from './move_rename' -export { readFileContentTool } from './read_file_content' -export * from './types' -export { uploadFileTool } from './upload_file' -export { writeFileContentTool } from './write_file_content' +import { checkCommandExistsTool } from './check_command_exists' +import { checkFileExistsTool } from './check_file_exists' +import { createDirectoryTool } from './create_directory' +import { deleteFileTool } from './delete_file' +import { downloadFileTool } from './download_file' +import { executeCommandTool } from './execute_command' +import { executeScriptTool } from './execute_script' +import { getSystemInfoTool } from './get_system_info' +import { listDirectoryTool } from './list_directory' +import { moveRenameTool } from './move_rename' +import { readFileContentTool } from './read_file_content' +import { uploadFileTool } from './upload_file' +import { writeFileContentTool } from './write_file_content' + +export const sshCheckCommandExistsTool = checkCommandExistsTool +export const sshCheckFileExistsTool = checkFileExistsTool +export const sshCreateDirectoryTool = createDirectoryTool +export const sshDeleteFileTool = deleteFileTool +export const sshDownloadFileTool = downloadFileTool +export const sshExecuteCommandTool = executeCommandTool +export const sshExecuteScriptTool = executeScriptTool +export const sshGetSystemInfoTool = getSystemInfoTool +export const sshListDirectoryTool = listDirectoryTool +export const sshMoveRenameTool = moveRenameTool +export const sshReadFileContentTool = readFileContentTool +export const sshUploadFileTool = uploadFileTool +export const sshWriteFileContentTool = writeFileContentTool