mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-28 03:00:29 -04:00
improvement(tools): added visibility for tools that were missing it, added new google and github tools (#2874)
* improvement(tools): added visibility for tools that were missing it, added new google tools * fixed the name for google forms * revert schema enrichers change * fixed block ordering
This commit is contained in:
@@ -11,15 +11,18 @@ export const a2aCancelTaskTool: ToolConfig<A2ACancelTaskParams, A2ACancelTaskRes
|
||||
agentUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'The A2A agent endpoint URL',
|
||||
},
|
||||
taskId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Task ID to cancel',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
visibility: 'user-only',
|
||||
description: 'API key for authentication',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -14,20 +14,24 @@ export const a2aDeletePushNotificationTool: ToolConfig<
|
||||
agentUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'The A2A agent endpoint URL',
|
||||
},
|
||||
taskId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Task ID to delete notification config for',
|
||||
},
|
||||
pushNotificationConfigId: {
|
||||
type: 'string',
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Push notification configuration ID to delete (optional - server can derive from taskId)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
visibility: 'user-only',
|
||||
description: 'API key for authentication',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -11,10 +11,12 @@ export const a2aGetAgentCardTool: ToolConfig<A2AGetAgentCardParams, A2AGetAgentC
|
||||
agentUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'The A2A agent endpoint URL',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
visibility: 'user-only',
|
||||
description: 'API key for authentication (if required)',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -14,15 +14,18 @@ export const a2aGetPushNotificationTool: ToolConfig<
|
||||
agentUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'The A2A agent endpoint URL',
|
||||
},
|
||||
taskId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Task ID to get notification config for',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
visibility: 'user-only',
|
||||
description: 'API key for authentication',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -11,19 +11,23 @@ export const a2aGetTaskTool: ToolConfig<A2AGetTaskParams, A2AGetTaskResponse> =
|
||||
agentUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'The A2A agent endpoint URL',
|
||||
},
|
||||
taskId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Task ID to query',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
visibility: 'user-only',
|
||||
description: 'API key for authentication',
|
||||
},
|
||||
historyLength: {
|
||||
type: 'number',
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of history messages to include',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -11,15 +11,18 @@ export const a2aResubscribeTool: ToolConfig<A2AResubscribeParams, A2AResubscribe
|
||||
agentUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'The A2A agent endpoint URL',
|
||||
},
|
||||
taskId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Task ID to resubscribe to',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
visibility: 'user-only',
|
||||
description: 'API key for authentication',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -11,31 +11,38 @@ export const a2aSendMessageTool: ToolConfig<A2ASendMessageParams, A2ASendMessage
|
||||
agentUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'The A2A agent endpoint URL',
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Message to send to the agent',
|
||||
},
|
||||
taskId: {
|
||||
type: 'string',
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Task ID for continuing an existing task',
|
||||
},
|
||||
contextId: {
|
||||
type: 'string',
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Context ID for conversation continuity',
|
||||
},
|
||||
data: {
|
||||
type: 'string',
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Structured data to include with the message (JSON string)',
|
||||
},
|
||||
files: {
|
||||
type: 'array',
|
||||
visibility: 'user-only',
|
||||
description: 'Files to include with the message',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
visibility: 'user-only',
|
||||
description: 'API key for authentication',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -14,24 +14,29 @@ export const a2aSetPushNotificationTool: ToolConfig<
|
||||
agentUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'The A2A agent endpoint URL',
|
||||
},
|
||||
taskId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Task ID to configure notifications for',
|
||||
},
|
||||
webhookUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'HTTPS webhook URL to receive notifications',
|
||||
},
|
||||
token: {
|
||||
type: 'string',
|
||||
visibility: 'user-only',
|
||||
description: 'Token for webhook validation',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
visibility: 'user-only',
|
||||
description: 'API key for authentication',
|
||||
},
|
||||
},
|
||||
|
||||
115
apps/sim/tools/github/check_star.ts
Normal file
115
apps/sim/tools/github/check_star.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface CheckStarParams {
|
||||
owner: string
|
||||
repo: string
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface CheckStarResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
starred: boolean
|
||||
owner: string
|
||||
repo: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const checkStarTool: ToolConfig<CheckStarParams, CheckStarResponse> = {
|
||||
id: 'github_check_star',
|
||||
name: 'GitHub Check Star',
|
||||
description: 'Check if you have starred a repository',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.github.com/user/starred/${params.owner}/${params.repo}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response, params) => {
|
||||
const starred = response.status === 204
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content: starred
|
||||
? `You have starred ${params?.owner}/${params?.repo}`
|
||||
: `You have not starred ${params?.owner}/${params?.repo}`,
|
||||
metadata: {
|
||||
starred,
|
||||
owner: params?.owner ?? '',
|
||||
repo: params?.repo ?? '',
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable result' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Check star metadata',
|
||||
properties: {
|
||||
starred: { type: 'boolean', description: 'Whether you have starred the repo' },
|
||||
owner: { type: 'string', description: 'Repository owner' },
|
||||
repo: { type: 'string', description: 'Repository name' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const checkStarV2Tool: ToolConfig<CheckStarParams, any> = {
|
||||
id: 'github_check_star_v2',
|
||||
name: checkStarTool.name,
|
||||
description: checkStarTool.description,
|
||||
version: '2.0.0',
|
||||
params: checkStarTool.params,
|
||||
request: checkStarTool.request,
|
||||
|
||||
transformResponse: async (response: Response, params) => {
|
||||
const starred = response.status === 204
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
starred,
|
||||
owner: params?.owner ?? '',
|
||||
repo: params?.repo ?? '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
starred: { type: 'boolean', description: 'Whether you have starred the repo' },
|
||||
owner: { type: 'string', description: 'Repository owner' },
|
||||
repo: { type: 'string', description: 'Repository name' },
|
||||
},
|
||||
}
|
||||
256
apps/sim/tools/github/compare_commits.ts
Normal file
256
apps/sim/tools/github/compare_commits.ts
Normal file
@@ -0,0 +1,256 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface CompareCommitsParams {
|
||||
owner: string
|
||||
repo: string
|
||||
base: string
|
||||
head: string
|
||||
per_page?: number
|
||||
page?: number
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface CompareCommitsResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
status: string
|
||||
ahead_by: number
|
||||
behind_by: number
|
||||
total_commits: number
|
||||
html_url: string
|
||||
diff_url: string
|
||||
patch_url: string
|
||||
base_commit: { sha: string; html_url: string }
|
||||
merge_base_commit: { sha: string; html_url: string }
|
||||
commits: Array<{
|
||||
sha: string
|
||||
html_url: string
|
||||
message: string
|
||||
author: { login?: string; name: string }
|
||||
}>
|
||||
files: Array<{
|
||||
filename: string
|
||||
status: string
|
||||
additions: number
|
||||
deletions: number
|
||||
changes: number
|
||||
}>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const compareCommitsTool: ToolConfig<CompareCommitsParams, CompareCommitsResponse> = {
|
||||
id: 'github_compare_commits',
|
||||
name: 'GitHub Compare Commits',
|
||||
description:
|
||||
'Compare two commits or branches to see the diff, commits between them, and changed files',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
base: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Base branch/tag/SHA for comparison',
|
||||
},
|
||||
head: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Head branch/tag/SHA for comparison',
|
||||
},
|
||||
per_page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Results per page for files (max 100, default: 30)',
|
||||
default: 30,
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number for files (default: 1)',
|
||||
default: 1,
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/compare/${params.base}...${params.head}`
|
||||
)
|
||||
if (params.per_page) url.searchParams.append('per_page', String(params.per_page))
|
||||
if (params.page) url.searchParams.append('page', String(params.page))
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const commits = (data.commits ?? []).map((c: any) => ({
|
||||
sha: c.sha,
|
||||
html_url: c.html_url,
|
||||
message: c.commit.message,
|
||||
author: {
|
||||
login: c.author?.login,
|
||||
name: c.commit.author.name,
|
||||
},
|
||||
}))
|
||||
|
||||
const files = (data.files ?? []).map((f: any) => ({
|
||||
filename: f.filename,
|
||||
status: f.status,
|
||||
additions: f.additions,
|
||||
deletions: f.deletions,
|
||||
changes: f.changes,
|
||||
}))
|
||||
|
||||
const metadata = {
|
||||
status: data.status,
|
||||
ahead_by: data.ahead_by,
|
||||
behind_by: data.behind_by,
|
||||
total_commits: data.total_commits,
|
||||
html_url: data.html_url,
|
||||
diff_url: data.diff_url,
|
||||
patch_url: data.patch_url,
|
||||
base_commit: {
|
||||
sha: data.base_commit.sha,
|
||||
html_url: data.base_commit.html_url,
|
||||
},
|
||||
merge_base_commit: {
|
||||
sha: data.merge_base_commit.sha,
|
||||
html_url: data.merge_base_commit.html_url,
|
||||
},
|
||||
commits,
|
||||
files,
|
||||
}
|
||||
|
||||
const content = `Comparing ${data.base_commit.sha.substring(0, 7)}...${data.commits?.length > 0 ? data.commits[data.commits.length - 1].sha.substring(0, 7) : 'HEAD'}
|
||||
Status: ${data.status} | Ahead: ${data.ahead_by} | Behind: ${data.behind_by}
|
||||
Total commits: ${data.total_commits} | Files changed: ${files.length}
|
||||
${data.html_url}
|
||||
|
||||
Commits:
|
||||
${commits.map((c: any) => ` ${c.sha.substring(0, 7)} - ${c.message.split('\n')[0]}`).join('\n')}
|
||||
|
||||
Files changed:
|
||||
${files.map((f: any) => ` ${f.status}: ${f.filename} (+${f.additions} -${f.deletions})`).join('\n')}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable comparison' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Comparison metadata',
|
||||
properties: {
|
||||
status: { type: 'string', description: 'ahead, behind, identical, or diverged' },
|
||||
ahead_by: { type: 'number', description: 'Commits ahead' },
|
||||
behind_by: { type: 'number', description: 'Commits behind' },
|
||||
total_commits: { type: 'number', description: 'Total commits between' },
|
||||
html_url: { type: 'string', description: 'GitHub web URL' },
|
||||
diff_url: { type: 'string', description: 'Diff URL' },
|
||||
patch_url: { type: 'string', description: 'Patch URL' },
|
||||
base_commit: { type: 'object', description: 'Base commit info' },
|
||||
merge_base_commit: { type: 'object', description: 'Merge base commit info' },
|
||||
commits: {
|
||||
type: 'array',
|
||||
description: 'Commits between base and head',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
sha: { type: 'string', description: 'Commit SHA' },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
message: { type: 'string', description: 'Commit message' },
|
||||
author: { type: 'object', description: 'Author info' },
|
||||
},
|
||||
},
|
||||
},
|
||||
files: {
|
||||
type: 'array',
|
||||
description: 'Changed files',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
filename: { type: 'string', description: 'File path' },
|
||||
status: { type: 'string', description: 'Change type' },
|
||||
additions: { type: 'number', description: 'Lines added' },
|
||||
deletions: { type: 'number', description: 'Lines deleted' },
|
||||
changes: { type: 'number', description: 'Total changes' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const compareCommitsV2Tool: ToolConfig<CompareCommitsParams, any> = {
|
||||
id: 'github_compare_commits_v2',
|
||||
name: compareCommitsTool.name,
|
||||
description: compareCommitsTool.description,
|
||||
version: '2.0.0',
|
||||
params: compareCommitsTool.params,
|
||||
request: compareCommitsTool.request,
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
...data,
|
||||
commits: data.commits ?? [],
|
||||
files: data.files ?? [],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
status: { type: 'string', description: 'Comparison status' },
|
||||
ahead_by: { type: 'number', description: 'Commits ahead' },
|
||||
behind_by: { type: 'number', description: 'Commits behind' },
|
||||
total_commits: { type: 'number', description: 'Total commits' },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
diff_url: { type: 'string', description: 'Diff URL' },
|
||||
patch_url: { type: 'string', description: 'Patch URL' },
|
||||
base_commit: { type: 'object', description: 'Base commit' },
|
||||
merge_base_commit: { type: 'object', description: 'Merge base' },
|
||||
commits: { type: 'array', description: 'Commits between' },
|
||||
files: { type: 'array', description: 'Changed files' },
|
||||
},
|
||||
}
|
||||
138
apps/sim/tools/github/create_comment_reaction.ts
Normal file
138
apps/sim/tools/github/create_comment_reaction.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface CreateCommentReactionParams {
|
||||
owner: string
|
||||
repo: string
|
||||
comment_id: number
|
||||
content: '+1' | '-1' | 'laugh' | 'confused' | 'heart' | 'hooray' | 'rocket' | 'eyes'
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface CreateCommentReactionResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
id: number
|
||||
user: { login: string }
|
||||
content: string
|
||||
created_at: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const createCommentReactionTool: ToolConfig<
|
||||
CreateCommentReactionParams,
|
||||
CreateCommentReactionResponse
|
||||
> = {
|
||||
id: 'github_create_comment_reaction',
|
||||
name: 'GitHub Create Comment Reaction',
|
||||
description: 'Add a reaction to an issue comment',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
comment_id: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comment ID',
|
||||
},
|
||||
content: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Reaction type: +1 (thumbs up), -1 (thumbs down), laugh, confused, heart, hooray, rocket, eyes',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/issues/comments/${params.comment_id}/reactions`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.squirrel-girl-preview+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
body: (params) => ({
|
||||
content: params.content,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const content = `Added ${data.content} reaction to comment by ${data.user?.login ?? 'unknown'}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
id: data.id,
|
||||
user: { login: data.user?.login ?? 'unknown' },
|
||||
content: data.content,
|
||||
created_at: data.created_at,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable result' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Reaction metadata',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'Reaction ID' },
|
||||
user: { type: 'object', description: 'User who reacted' },
|
||||
content: { type: 'string', description: 'Reaction type' },
|
||||
created_at: { type: 'string', description: 'Creation date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const createCommentReactionV2Tool: ToolConfig<CreateCommentReactionParams, any> = {
|
||||
id: 'github_create_comment_reaction_v2',
|
||||
name: createCommentReactionTool.name,
|
||||
description: createCommentReactionTool.description,
|
||||
version: '2.0.0',
|
||||
params: createCommentReactionTool.params,
|
||||
request: createCommentReactionTool.request,
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: data,
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'number', description: 'Reaction ID' },
|
||||
user: { type: 'object', description: 'User who reacted' },
|
||||
content: { type: 'string', description: 'Reaction type' },
|
||||
created_at: { type: 'string', description: 'Creation date' },
|
||||
},
|
||||
}
|
||||
183
apps/sim/tools/github/create_gist.ts
Normal file
183
apps/sim/tools/github/create_gist.ts
Normal file
@@ -0,0 +1,183 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface CreateGistParams {
|
||||
description?: string
|
||||
files: string
|
||||
public?: boolean
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface CreateGistResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
id: string
|
||||
html_url: string
|
||||
git_pull_url: string
|
||||
git_push_url: string
|
||||
description: string | null
|
||||
public: boolean
|
||||
created_at: string
|
||||
updated_at: string
|
||||
files: Record<
|
||||
string,
|
||||
{ filename: string; type: string; language: string | null; size: number }
|
||||
>
|
||||
owner: { login: string }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const createGistTool: ToolConfig<CreateGistParams, CreateGistResponse> = {
|
||||
id: 'github_create_gist',
|
||||
name: 'GitHub Create Gist',
|
||||
description: 'Create a new gist with one or more files',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
description: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Description of the gist',
|
||||
},
|
||||
files: {
|
||||
type: 'json',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'JSON object with filenames as keys and content as values. Example: {"file.txt": {"content": "Hello"}}',
|
||||
},
|
||||
public: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether the gist is public (default: false)',
|
||||
default: false,
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => 'https://api.github.com/gists',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
body: (params) => {
|
||||
const filesObj = typeof params.files === 'string' ? JSON.parse(params.files) : params.files
|
||||
return {
|
||||
description: params.description,
|
||||
public: params.public ?? false,
|
||||
files: filesObj,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const files: Record<
|
||||
string,
|
||||
{ filename: string; type: string; language: string | null; size: number }
|
||||
> = {}
|
||||
for (const [key, value] of Object.entries(data.files ?? {})) {
|
||||
const file = value as any
|
||||
files[key] = {
|
||||
filename: file.filename,
|
||||
type: file.type,
|
||||
language: file.language ?? null,
|
||||
size: file.size,
|
||||
}
|
||||
}
|
||||
|
||||
const metadata = {
|
||||
id: data.id,
|
||||
html_url: data.html_url,
|
||||
git_pull_url: data.git_pull_url,
|
||||
git_push_url: data.git_push_url,
|
||||
description: data.description ?? null,
|
||||
public: data.public,
|
||||
created_at: data.created_at,
|
||||
updated_at: data.updated_at,
|
||||
files,
|
||||
owner: { login: data.owner?.login ?? 'unknown' },
|
||||
}
|
||||
|
||||
const content = `Created gist: ${data.html_url}
|
||||
Description: ${data.description ?? 'No description'}
|
||||
Public: ${data.public}
|
||||
Files: ${Object.keys(files).join(', ')}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable result' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Gist metadata',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Gist ID' },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
git_pull_url: { type: 'string', description: 'Git pull URL' },
|
||||
git_push_url: { type: 'string', description: 'Git push URL' },
|
||||
description: { type: 'string', description: 'Description', optional: true },
|
||||
public: { type: 'boolean', description: 'Is public' },
|
||||
created_at: { type: 'string', description: 'Creation date' },
|
||||
updated_at: { type: 'string', description: 'Update date' },
|
||||
files: { type: 'object', description: 'Files in gist' },
|
||||
owner: { type: 'object', description: 'Owner info' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const createGistV2Tool: ToolConfig<CreateGistParams, any> = {
|
||||
id: 'github_create_gist_v2',
|
||||
name: createGistTool.name,
|
||||
description: createGistTool.description,
|
||||
version: '2.0.0',
|
||||
params: createGistTool.params,
|
||||
request: createGistTool.request,
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
...data,
|
||||
description: data.description ?? null,
|
||||
files: data.files ?? {},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Gist ID' },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
git_pull_url: { type: 'string', description: 'Git pull URL' },
|
||||
git_push_url: { type: 'string', description: 'Git push URL' },
|
||||
description: { type: 'string', description: 'Description', optional: true },
|
||||
public: { type: 'boolean', description: 'Is public' },
|
||||
created_at: { type: 'string', description: 'Creation date' },
|
||||
updated_at: { type: 'string', description: 'Update date' },
|
||||
files: { type: 'object', description: 'Files in gist' },
|
||||
owner: { type: 'object', description: 'Owner info' },
|
||||
},
|
||||
}
|
||||
138
apps/sim/tools/github/create_issue_reaction.ts
Normal file
138
apps/sim/tools/github/create_issue_reaction.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface CreateIssueReactionParams {
|
||||
owner: string
|
||||
repo: string
|
||||
issue_number: number
|
||||
content: '+1' | '-1' | 'laugh' | 'confused' | 'heart' | 'hooray' | 'rocket' | 'eyes'
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface CreateIssueReactionResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
id: number
|
||||
user: { login: string }
|
||||
content: string
|
||||
created_at: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const createIssueReactionTool: ToolConfig<
|
||||
CreateIssueReactionParams,
|
||||
CreateIssueReactionResponse
|
||||
> = {
|
||||
id: 'github_create_issue_reaction',
|
||||
name: 'GitHub Create Issue Reaction',
|
||||
description: 'Add a reaction to an issue',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
issue_number: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Issue number',
|
||||
},
|
||||
content: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Reaction type: +1 (thumbs up), -1 (thumbs down), laugh, confused, heart, hooray, rocket, eyes',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/issues/${params.issue_number}/reactions`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.squirrel-girl-preview+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
body: (params) => ({
|
||||
content: params.content,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const content = `Added ${data.content} reaction to issue by ${data.user?.login ?? 'unknown'}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
id: data.id,
|
||||
user: { login: data.user?.login ?? 'unknown' },
|
||||
content: data.content,
|
||||
created_at: data.created_at,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable result' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Reaction metadata',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'Reaction ID' },
|
||||
user: { type: 'object', description: 'User who reacted' },
|
||||
content: { type: 'string', description: 'Reaction type' },
|
||||
created_at: { type: 'string', description: 'Creation date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const createIssueReactionV2Tool: ToolConfig<CreateIssueReactionParams, any> = {
|
||||
id: 'github_create_issue_reaction_v2',
|
||||
name: createIssueReactionTool.name,
|
||||
description: createIssueReactionTool.description,
|
||||
version: '2.0.0',
|
||||
params: createIssueReactionTool.params,
|
||||
request: createIssueReactionTool.request,
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: data,
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'number', description: 'Reaction ID' },
|
||||
user: { type: 'object', description: 'User who reacted' },
|
||||
content: { type: 'string', description: 'Reaction type' },
|
||||
created_at: { type: 'string', description: 'Creation date' },
|
||||
},
|
||||
}
|
||||
182
apps/sim/tools/github/create_milestone.ts
Normal file
182
apps/sim/tools/github/create_milestone.ts
Normal file
@@ -0,0 +1,182 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface CreateMilestoneParams {
|
||||
owner: string
|
||||
repo: string
|
||||
title: string
|
||||
state?: 'open' | 'closed'
|
||||
description?: string
|
||||
due_on?: string
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface CreateMilestoneResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
number: number
|
||||
title: string
|
||||
description: string | null
|
||||
state: string
|
||||
html_url: string
|
||||
due_on: string | null
|
||||
open_issues: number
|
||||
closed_issues: number
|
||||
created_at: string
|
||||
creator: { login: string }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const createMilestoneTool: ToolConfig<CreateMilestoneParams, CreateMilestoneResponse> = {
|
||||
id: 'github_create_milestone',
|
||||
name: 'GitHub Create Milestone',
|
||||
description: 'Create a milestone in a repository',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Milestone title',
|
||||
},
|
||||
state: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'State: open or closed (default: open)',
|
||||
default: 'open',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Milestone description',
|
||||
},
|
||||
due_on: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Due date (ISO 8601 format, e.g., 2024-12-31T23:59:59Z)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.github.com/repos/${params.owner}/${params.repo}/milestones`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
body: (params) => ({
|
||||
title: params.title,
|
||||
state: params.state ?? 'open',
|
||||
description: params.description,
|
||||
due_on: params.due_on,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const content = `Created milestone: ${data.title}
|
||||
Number: ${data.number}
|
||||
State: ${data.state}
|
||||
Due: ${data.due_on ?? 'No due date'}
|
||||
${data.html_url}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
number: data.number,
|
||||
title: data.title,
|
||||
description: data.description ?? null,
|
||||
state: data.state,
|
||||
html_url: data.html_url,
|
||||
due_on: data.due_on ?? null,
|
||||
open_issues: data.open_issues,
|
||||
closed_issues: data.closed_issues,
|
||||
created_at: data.created_at,
|
||||
creator: { login: data.creator?.login ?? 'unknown' },
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable result' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Milestone metadata',
|
||||
properties: {
|
||||
number: { type: 'number', description: 'Milestone number' },
|
||||
title: { type: 'string', description: 'Title' },
|
||||
description: { type: 'string', description: 'Description', optional: true },
|
||||
state: { type: 'string', description: 'State' },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
due_on: { type: 'string', description: 'Due date', optional: true },
|
||||
open_issues: { type: 'number', description: 'Open issues count' },
|
||||
closed_issues: { type: 'number', description: 'Closed issues count' },
|
||||
created_at: { type: 'string', description: 'Creation date' },
|
||||
creator: { type: 'object', description: 'Creator info' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const createMilestoneV2Tool: ToolConfig<CreateMilestoneParams, any> = {
|
||||
id: 'github_create_milestone_v2',
|
||||
name: createMilestoneTool.name,
|
||||
description: createMilestoneTool.description,
|
||||
version: '2.0.0',
|
||||
params: createMilestoneTool.params,
|
||||
request: createMilestoneTool.request,
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
...data,
|
||||
description: data.description ?? null,
|
||||
due_on: data.due_on ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
number: { type: 'number', description: 'Milestone number' },
|
||||
title: { type: 'string', description: 'Title' },
|
||||
description: { type: 'string', description: 'Description', optional: true },
|
||||
state: { type: 'string', description: 'State' },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
due_on: { type: 'string', description: 'Due date', optional: true },
|
||||
open_issues: { type: 'number', description: 'Open issues' },
|
||||
closed_issues: { type: 'number', description: 'Closed issues' },
|
||||
creator: { type: 'object', description: 'Creator' },
|
||||
},
|
||||
}
|
||||
128
apps/sim/tools/github/delete_comment_reaction.ts
Normal file
128
apps/sim/tools/github/delete_comment_reaction.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface DeleteCommentReactionParams {
|
||||
owner: string
|
||||
repo: string
|
||||
comment_id: number
|
||||
reaction_id: number
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface DeleteCommentReactionResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
deleted: boolean
|
||||
reaction_id: number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const deleteCommentReactionTool: ToolConfig<
|
||||
DeleteCommentReactionParams,
|
||||
DeleteCommentReactionResponse
|
||||
> = {
|
||||
id: 'github_delete_comment_reaction',
|
||||
name: 'GitHub Delete Comment Reaction',
|
||||
description: 'Remove a reaction from an issue comment',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
comment_id: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comment ID',
|
||||
},
|
||||
reaction_id: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Reaction ID to delete',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/issues/comments/${params.comment_id}/reactions/${params.reaction_id}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.squirrel-girl-preview+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response, params) => {
|
||||
const deleted = response.status === 204
|
||||
|
||||
return {
|
||||
success: deleted,
|
||||
output: {
|
||||
content: deleted
|
||||
? `Successfully deleted reaction ${params?.reaction_id}`
|
||||
: `Failed to delete reaction ${params?.reaction_id}`,
|
||||
metadata: {
|
||||
deleted,
|
||||
reaction_id: params?.reaction_id ?? 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable result' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Delete operation metadata',
|
||||
properties: {
|
||||
deleted: { type: 'boolean', description: 'Whether deletion succeeded' },
|
||||
reaction_id: { type: 'number', description: 'The deleted reaction ID' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const deleteCommentReactionV2Tool: ToolConfig<DeleteCommentReactionParams, any> = {
|
||||
id: 'github_delete_comment_reaction_v2',
|
||||
name: deleteCommentReactionTool.name,
|
||||
description: deleteCommentReactionTool.description,
|
||||
version: '2.0.0',
|
||||
params: deleteCommentReactionTool.params,
|
||||
request: deleteCommentReactionTool.request,
|
||||
|
||||
transformResponse: async (response: Response, params) => {
|
||||
const deleted = response.status === 204
|
||||
return {
|
||||
success: deleted,
|
||||
output: {
|
||||
deleted,
|
||||
reaction_id: params?.reaction_id ?? 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
deleted: { type: 'boolean', description: 'Whether deletion succeeded' },
|
||||
reaction_id: { type: 'number', description: 'The deleted reaction ID' },
|
||||
},
|
||||
}
|
||||
103
apps/sim/tools/github/delete_gist.ts
Normal file
103
apps/sim/tools/github/delete_gist.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface DeleteGistParams {
|
||||
gist_id: string
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface DeleteGistResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
deleted: boolean
|
||||
gist_id: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const deleteGistTool: ToolConfig<DeleteGistParams, DeleteGistResponse> = {
|
||||
id: 'github_delete_gist',
|
||||
name: 'GitHub Delete Gist',
|
||||
description: 'Delete a gist by ID',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
gist_id: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The gist ID to delete',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.github.com/gists/${params.gist_id?.trim()}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response, params) => {
|
||||
const deleted = response.status === 204
|
||||
|
||||
return {
|
||||
success: deleted,
|
||||
output: {
|
||||
content: deleted
|
||||
? `Successfully deleted gist ${params?.gist_id}`
|
||||
: `Failed to delete gist ${params?.gist_id}`,
|
||||
metadata: {
|
||||
deleted,
|
||||
gist_id: params?.gist_id ?? '',
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable result' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Delete operation metadata',
|
||||
properties: {
|
||||
deleted: { type: 'boolean', description: 'Whether deletion succeeded' },
|
||||
gist_id: { type: 'string', description: 'The deleted gist ID' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const deleteGistV2Tool: ToolConfig<DeleteGistParams, any> = {
|
||||
id: 'github_delete_gist_v2',
|
||||
name: deleteGistTool.name,
|
||||
description: deleteGistTool.description,
|
||||
version: '2.0.0',
|
||||
params: deleteGistTool.params,
|
||||
request: deleteGistTool.request,
|
||||
|
||||
transformResponse: async (response: Response, params) => {
|
||||
const deleted = response.status === 204
|
||||
return {
|
||||
success: deleted,
|
||||
output: {
|
||||
deleted,
|
||||
gist_id: params?.gist_id ?? '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
deleted: { type: 'boolean', description: 'Whether deletion succeeded' },
|
||||
gist_id: { type: 'string', description: 'The deleted gist ID' },
|
||||
},
|
||||
}
|
||||
128
apps/sim/tools/github/delete_issue_reaction.ts
Normal file
128
apps/sim/tools/github/delete_issue_reaction.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface DeleteIssueReactionParams {
|
||||
owner: string
|
||||
repo: string
|
||||
issue_number: number
|
||||
reaction_id: number
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface DeleteIssueReactionResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
deleted: boolean
|
||||
reaction_id: number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const deleteIssueReactionTool: ToolConfig<
|
||||
DeleteIssueReactionParams,
|
||||
DeleteIssueReactionResponse
|
||||
> = {
|
||||
id: 'github_delete_issue_reaction',
|
||||
name: 'GitHub Delete Issue Reaction',
|
||||
description: 'Remove a reaction from an issue',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
issue_number: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Issue number',
|
||||
},
|
||||
reaction_id: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Reaction ID to delete',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/issues/${params.issue_number}/reactions/${params.reaction_id}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.squirrel-girl-preview+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response, params) => {
|
||||
const deleted = response.status === 204
|
||||
|
||||
return {
|
||||
success: deleted,
|
||||
output: {
|
||||
content: deleted
|
||||
? `Successfully deleted reaction ${params?.reaction_id}`
|
||||
: `Failed to delete reaction ${params?.reaction_id}`,
|
||||
metadata: {
|
||||
deleted,
|
||||
reaction_id: params?.reaction_id ?? 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable result' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Delete operation metadata',
|
||||
properties: {
|
||||
deleted: { type: 'boolean', description: 'Whether deletion succeeded' },
|
||||
reaction_id: { type: 'number', description: 'The deleted reaction ID' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const deleteIssueReactionV2Tool: ToolConfig<DeleteIssueReactionParams, any> = {
|
||||
id: 'github_delete_issue_reaction_v2',
|
||||
name: deleteIssueReactionTool.name,
|
||||
description: deleteIssueReactionTool.description,
|
||||
version: '2.0.0',
|
||||
params: deleteIssueReactionTool.params,
|
||||
request: deleteIssueReactionTool.request,
|
||||
|
||||
transformResponse: async (response: Response, params) => {
|
||||
const deleted = response.status === 204
|
||||
return {
|
||||
success: deleted,
|
||||
output: {
|
||||
deleted,
|
||||
reaction_id: params?.reaction_id ?? 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
deleted: { type: 'boolean', description: 'Whether deletion succeeded' },
|
||||
reaction_id: { type: 'number', description: 'The deleted reaction ID' },
|
||||
},
|
||||
}
|
||||
118
apps/sim/tools/github/delete_milestone.ts
Normal file
118
apps/sim/tools/github/delete_milestone.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface DeleteMilestoneParams {
|
||||
owner: string
|
||||
repo: string
|
||||
milestone_number: number
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface DeleteMilestoneResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
deleted: boolean
|
||||
milestone_number: number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const deleteMilestoneTool: ToolConfig<DeleteMilestoneParams, DeleteMilestoneResponse> = {
|
||||
id: 'github_delete_milestone',
|
||||
name: 'GitHub Delete Milestone',
|
||||
description: 'Delete a milestone from a repository',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
milestone_number: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Milestone number to delete',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/milestones/${params.milestone_number}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response, params) => {
|
||||
const deleted = response.status === 204
|
||||
|
||||
return {
|
||||
success: deleted,
|
||||
output: {
|
||||
content: deleted
|
||||
? `Successfully deleted milestone #${params?.milestone_number}`
|
||||
: `Failed to delete milestone #${params?.milestone_number}`,
|
||||
metadata: {
|
||||
deleted,
|
||||
milestone_number: params?.milestone_number ?? 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable result' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Delete operation metadata',
|
||||
properties: {
|
||||
deleted: { type: 'boolean', description: 'Whether deletion succeeded' },
|
||||
milestone_number: { type: 'number', description: 'The deleted milestone number' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const deleteMilestoneV2Tool: ToolConfig<DeleteMilestoneParams, any> = {
|
||||
id: 'github_delete_milestone_v2',
|
||||
name: deleteMilestoneTool.name,
|
||||
description: deleteMilestoneTool.description,
|
||||
version: '2.0.0',
|
||||
params: deleteMilestoneTool.params,
|
||||
request: deleteMilestoneTool.request,
|
||||
|
||||
transformResponse: async (response: Response, params) => {
|
||||
const deleted = response.status === 204
|
||||
return {
|
||||
success: deleted,
|
||||
output: {
|
||||
deleted,
|
||||
milestone_number: params?.milestone_number ?? 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
deleted: { type: 'boolean', description: 'Whether deletion succeeded' },
|
||||
milestone_number: { type: 'number', description: 'The deleted milestone number' },
|
||||
},
|
||||
}
|
||||
131
apps/sim/tools/github/fork_gist.ts
Normal file
131
apps/sim/tools/github/fork_gist.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface ForkGistParams {
|
||||
gist_id: string
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface ForkGistResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
id: string
|
||||
html_url: string
|
||||
git_pull_url: string
|
||||
description: string | null
|
||||
public: boolean
|
||||
created_at: string
|
||||
owner: { login: string }
|
||||
files: string[]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const forkGistTool: ToolConfig<ForkGistParams, ForkGistResponse> = {
|
||||
id: 'github_fork_gist',
|
||||
name: 'GitHub Fork Gist',
|
||||
description: 'Fork a gist to create your own copy',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
gist_id: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The gist ID to fork',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.github.com/gists/${params.gist_id?.trim()}/forks`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const files = Object.keys(data.files ?? {})
|
||||
|
||||
const content = `Forked gist: ${data.html_url}
|
||||
Description: ${data.description ?? 'No description'}
|
||||
Files: ${files.join(', ')}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
id: data.id,
|
||||
html_url: data.html_url,
|
||||
git_pull_url: data.git_pull_url,
|
||||
description: data.description ?? null,
|
||||
public: data.public,
|
||||
created_at: data.created_at,
|
||||
owner: { login: data.owner?.login ?? 'unknown' },
|
||||
files,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable result' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Forked gist metadata',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'New gist ID' },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
git_pull_url: { type: 'string', description: 'Git pull URL' },
|
||||
description: { type: 'string', description: 'Description', optional: true },
|
||||
public: { type: 'boolean', description: 'Is public' },
|
||||
created_at: { type: 'string', description: 'Creation date' },
|
||||
owner: { type: 'object', description: 'Owner info' },
|
||||
files: { type: 'array', description: 'File names' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const forkGistV2Tool: ToolConfig<ForkGistParams, any> = {
|
||||
id: 'github_fork_gist_v2',
|
||||
name: forkGistTool.name,
|
||||
description: forkGistTool.description,
|
||||
version: '2.0.0',
|
||||
params: forkGistTool.params,
|
||||
request: forkGistTool.request,
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
...data,
|
||||
description: data.description ?? null,
|
||||
files: data.files ?? {},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'New gist ID' },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
description: { type: 'string', description: 'Description', optional: true },
|
||||
public: { type: 'boolean', description: 'Is public' },
|
||||
created_at: { type: 'string', description: 'Creation date' },
|
||||
owner: { type: 'object', description: 'Owner info' },
|
||||
files: { type: 'object', description: 'Files' },
|
||||
},
|
||||
}
|
||||
179
apps/sim/tools/github/fork_repo.ts
Normal file
179
apps/sim/tools/github/fork_repo.ts
Normal file
@@ -0,0 +1,179 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface ForkRepoParams {
|
||||
owner: string
|
||||
repo: string
|
||||
organization?: string
|
||||
name?: string
|
||||
default_branch_only?: boolean
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface ForkRepoResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
id: number
|
||||
full_name: string
|
||||
html_url: string
|
||||
clone_url: string
|
||||
ssh_url: string
|
||||
default_branch: string
|
||||
fork: boolean
|
||||
parent: { full_name: string; html_url: string }
|
||||
owner: { login: string }
|
||||
created_at: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const forkRepoTool: ToolConfig<ForkRepoParams, ForkRepoResponse> = {
|
||||
id: 'github_fork_repo',
|
||||
name: 'GitHub Fork Repository',
|
||||
description: 'Fork a repository to your account or an organization',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner to fork from',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name to fork',
|
||||
},
|
||||
organization: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Organization to fork into (omit to fork to your account)',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Custom name for the forked repository',
|
||||
},
|
||||
default_branch_only: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only fork the default branch (default: false)',
|
||||
default: false,
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.github.com/repos/${params.owner}/${params.repo}/forks`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, any> = {}
|
||||
if (params.organization) body.organization = params.organization
|
||||
if (params.name) body.name = params.name
|
||||
if (params.default_branch_only !== undefined)
|
||||
body.default_branch_only = params.default_branch_only
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const content = `Forked repository: ${data.html_url}
|
||||
Forked from: ${data.parent?.full_name ?? 'unknown'}
|
||||
Clone URL: ${data.clone_url}
|
||||
Default branch: ${data.default_branch}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
id: data.id,
|
||||
full_name: data.full_name,
|
||||
html_url: data.html_url,
|
||||
clone_url: data.clone_url,
|
||||
ssh_url: data.ssh_url,
|
||||
default_branch: data.default_branch,
|
||||
fork: data.fork,
|
||||
parent: {
|
||||
full_name: data.parent?.full_name ?? '',
|
||||
html_url: data.parent?.html_url ?? '',
|
||||
},
|
||||
owner: { login: data.owner?.login ?? 'unknown' },
|
||||
created_at: data.created_at,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable result' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Forked repository metadata',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'Repository ID' },
|
||||
full_name: { type: 'string', description: 'Full name (owner/repo)' },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
clone_url: { type: 'string', description: 'HTTPS clone URL' },
|
||||
ssh_url: { type: 'string', description: 'SSH clone URL' },
|
||||
default_branch: { type: 'string', description: 'Default branch' },
|
||||
fork: { type: 'boolean', description: 'Is a fork' },
|
||||
parent: { type: 'object', description: 'Parent repository' },
|
||||
owner: { type: 'object', description: 'Owner info' },
|
||||
created_at: { type: 'string', description: 'Creation date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const forkRepoV2Tool: ToolConfig<ForkRepoParams, any> = {
|
||||
id: 'github_fork_repo_v2',
|
||||
name: forkRepoTool.name,
|
||||
description: forkRepoTool.description,
|
||||
version: '2.0.0',
|
||||
params: forkRepoTool.params,
|
||||
request: forkRepoTool.request,
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
...data,
|
||||
parent: data.parent ?? null,
|
||||
source: data.source ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'number', description: 'Repository ID' },
|
||||
full_name: { type: 'string', description: 'Full name' },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
clone_url: { type: 'string', description: 'Clone URL' },
|
||||
ssh_url: { type: 'string', description: 'SSH URL' },
|
||||
default_branch: { type: 'string', description: 'Default branch' },
|
||||
fork: { type: 'boolean', description: 'Is a fork' },
|
||||
parent: { type: 'object', description: 'Parent repository', optional: true },
|
||||
owner: { type: 'object', description: 'Owner' },
|
||||
},
|
||||
}
|
||||
202
apps/sim/tools/github/get_commit.ts
Normal file
202
apps/sim/tools/github/get_commit.ts
Normal file
@@ -0,0 +1,202 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface GetCommitParams {
|
||||
owner: string
|
||||
repo: string
|
||||
ref: string
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface GetCommitResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
sha: string
|
||||
html_url: string
|
||||
message: string
|
||||
author: { name: string; email: string; date: string; login?: string }
|
||||
committer: { name: string; email: string; date: string; login?: string }
|
||||
stats: { additions: number; deletions: number; total: number }
|
||||
files: Array<{
|
||||
filename: string
|
||||
status: string
|
||||
additions: number
|
||||
deletions: number
|
||||
changes: number
|
||||
patch?: string
|
||||
}>
|
||||
parents: Array<{ sha: string; html_url: string }>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const getCommitTool: ToolConfig<GetCommitParams, GetCommitResponse> = {
|
||||
id: 'github_get_commit',
|
||||
name: 'GitHub Get Commit',
|
||||
description: 'Get detailed information about a specific commit including files changed and stats',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
ref: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Commit SHA, branch name, or tag name',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/commits/${params.ref}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const files = (data.files ?? []).map((f: any) => ({
|
||||
filename: f.filename,
|
||||
status: f.status,
|
||||
additions: f.additions,
|
||||
deletions: f.deletions,
|
||||
changes: f.changes,
|
||||
patch: f.patch,
|
||||
}))
|
||||
|
||||
const metadata = {
|
||||
sha: data.sha,
|
||||
html_url: data.html_url,
|
||||
message: data.commit.message,
|
||||
author: {
|
||||
name: data.commit.author.name,
|
||||
email: data.commit.author.email,
|
||||
date: data.commit.author.date,
|
||||
login: data.author?.login,
|
||||
},
|
||||
committer: {
|
||||
name: data.commit.committer.name,
|
||||
email: data.commit.committer.email,
|
||||
date: data.commit.committer.date,
|
||||
login: data.committer?.login,
|
||||
},
|
||||
stats: data.stats ?? { additions: 0, deletions: 0, total: 0 },
|
||||
files,
|
||||
parents: data.parents.map((p: any) => ({ sha: p.sha, html_url: p.html_url })),
|
||||
}
|
||||
|
||||
const content = `Commit ${data.sha.substring(0, 7)}
|
||||
Message: ${data.commit.message.split('\n')[0]}
|
||||
Author: ${metadata.author.login ?? metadata.author.name} (${metadata.author.date})
|
||||
Stats: +${metadata.stats.additions} -${metadata.stats.deletions} (${files.length} files)
|
||||
${data.html_url}
|
||||
|
||||
Files changed:
|
||||
${files.map((f: any) => ` ${f.status}: ${f.filename} (+${f.additions} -${f.deletions})`).join('\n')}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable commit details' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Commit metadata',
|
||||
properties: {
|
||||
sha: { type: 'string', description: 'Full commit SHA' },
|
||||
html_url: { type: 'string', description: 'GitHub web URL' },
|
||||
message: { type: 'string', description: 'Commit message' },
|
||||
author: { type: 'object', description: 'Author info' },
|
||||
committer: { type: 'object', description: 'Committer info' },
|
||||
stats: {
|
||||
type: 'object',
|
||||
description: 'Change stats',
|
||||
properties: {
|
||||
additions: { type: 'number', description: 'Lines added' },
|
||||
deletions: { type: 'number', description: 'Lines deleted' },
|
||||
total: { type: 'number', description: 'Total changes' },
|
||||
},
|
||||
},
|
||||
files: {
|
||||
type: 'array',
|
||||
description: 'Changed files',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
filename: { type: 'string', description: 'File path' },
|
||||
status: { type: 'string', description: 'Change type' },
|
||||
additions: { type: 'number', description: 'Lines added' },
|
||||
deletions: { type: 'number', description: 'Lines deleted' },
|
||||
changes: { type: 'number', description: 'Total changes' },
|
||||
patch: { type: 'string', description: 'Diff patch', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
parents: { type: 'array', description: 'Parent commits' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const getCommitV2Tool: ToolConfig<GetCommitParams, any> = {
|
||||
id: 'github_get_commit_v2',
|
||||
name: getCommitTool.name,
|
||||
description: getCommitTool.description,
|
||||
version: '2.0.0',
|
||||
params: getCommitTool.params,
|
||||
request: getCommitTool.request,
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
...data,
|
||||
author: data.author ?? null,
|
||||
committer: data.committer ?? null,
|
||||
stats: data.stats ?? null,
|
||||
files: data.files ?? [],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
sha: { type: 'string', description: 'Commit SHA' },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
commit: { type: 'object', description: 'Commit data' },
|
||||
author: { type: 'object', description: 'GitHub user', optional: true },
|
||||
committer: { type: 'object', description: 'GitHub user', optional: true },
|
||||
stats: { type: 'object', description: 'Change stats', optional: true },
|
||||
files: { type: 'array', description: 'Changed files' },
|
||||
parents: { type: 'array', description: 'Parent commits' },
|
||||
},
|
||||
}
|
||||
177
apps/sim/tools/github/get_gist.ts
Normal file
177
apps/sim/tools/github/get_gist.ts
Normal file
@@ -0,0 +1,177 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface GetGistParams {
|
||||
gist_id: string
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface GetGistResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
id: string
|
||||
html_url: string
|
||||
git_pull_url: string
|
||||
git_push_url: string
|
||||
description: string | null
|
||||
public: boolean
|
||||
created_at: string
|
||||
updated_at: string
|
||||
files: Record<
|
||||
string,
|
||||
{ filename: string; type: string; language: string | null; size: number; content: string }
|
||||
>
|
||||
owner: { login: string }
|
||||
comments: number
|
||||
forks_url: string
|
||||
commits_url: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const getGistTool: ToolConfig<GetGistParams, GetGistResponse> = {
|
||||
id: 'github_get_gist',
|
||||
name: 'GitHub Get Gist',
|
||||
description: 'Get a gist by ID including its file contents',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
gist_id: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The gist ID',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.github.com/gists/${params.gist_id?.trim()}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const files: Record<
|
||||
string,
|
||||
{ filename: string; type: string; language: string | null; size: number; content: string }
|
||||
> = {}
|
||||
for (const [key, value] of Object.entries(data.files ?? {})) {
|
||||
const file = value as any
|
||||
files[key] = {
|
||||
filename: file.filename,
|
||||
type: file.type,
|
||||
language: file.language ?? null,
|
||||
size: file.size,
|
||||
content: file.content ?? '',
|
||||
}
|
||||
}
|
||||
|
||||
const metadata = {
|
||||
id: data.id,
|
||||
html_url: data.html_url,
|
||||
git_pull_url: data.git_pull_url,
|
||||
git_push_url: data.git_push_url,
|
||||
description: data.description ?? null,
|
||||
public: data.public,
|
||||
created_at: data.created_at,
|
||||
updated_at: data.updated_at,
|
||||
files,
|
||||
owner: { login: data.owner?.login ?? 'unknown' },
|
||||
comments: data.comments ?? 0,
|
||||
forks_url: data.forks_url,
|
||||
commits_url: data.commits_url,
|
||||
}
|
||||
|
||||
const fileList = Object.entries(files)
|
||||
.map(([name, f]) => `${name} (${f.language ?? 'unknown'}, ${f.size} bytes)`)
|
||||
.join(', ')
|
||||
|
||||
const content = `Gist: ${data.html_url}
|
||||
Description: ${data.description ?? 'No description'}
|
||||
Public: ${data.public} | Comments: ${data.comments ?? 0}
|
||||
Owner: ${data.owner?.login ?? 'unknown'}
|
||||
Files: ${fileList}
|
||||
|
||||
${Object.entries(files)
|
||||
.map(([name, f]) => `--- ${name} ---\n${f.content}`)
|
||||
.join('\n\n')}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable gist with file contents' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Gist metadata',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Gist ID' },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
git_pull_url: { type: 'string', description: 'Git pull URL' },
|
||||
git_push_url: { type: 'string', description: 'Git push URL' },
|
||||
description: { type: 'string', description: 'Description', optional: true },
|
||||
public: { type: 'boolean', description: 'Is public' },
|
||||
created_at: { type: 'string', description: 'Creation date' },
|
||||
updated_at: { type: 'string', description: 'Update date' },
|
||||
files: { type: 'object', description: 'Files with content' },
|
||||
owner: { type: 'object', description: 'Owner info' },
|
||||
comments: { type: 'number', description: 'Comment count' },
|
||||
forks_url: { type: 'string', description: 'Forks URL' },
|
||||
commits_url: { type: 'string', description: 'Commits URL' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const getGistV2Tool: ToolConfig<GetGistParams, any> = {
|
||||
id: 'github_get_gist_v2',
|
||||
name: getGistTool.name,
|
||||
description: getGistTool.description,
|
||||
version: '2.0.0',
|
||||
params: getGistTool.params,
|
||||
request: getGistTool.request,
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
...data,
|
||||
description: data.description ?? null,
|
||||
files: data.files ?? {},
|
||||
comments: data.comments ?? 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Gist ID' },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
description: { type: 'string', description: 'Description', optional: true },
|
||||
public: { type: 'boolean', description: 'Is public' },
|
||||
created_at: { type: 'string', description: 'Creation date' },
|
||||
updated_at: { type: 'string', description: 'Update date' },
|
||||
files: { type: 'object', description: 'Files with content' },
|
||||
owner: { type: 'object', description: 'Owner info' },
|
||||
comments: { type: 'number', description: 'Comment count' },
|
||||
},
|
||||
}
|
||||
167
apps/sim/tools/github/get_milestone.ts
Normal file
167
apps/sim/tools/github/get_milestone.ts
Normal file
@@ -0,0 +1,167 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface GetMilestoneParams {
|
||||
owner: string
|
||||
repo: string
|
||||
milestone_number: number
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface GetMilestoneResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
number: number
|
||||
title: string
|
||||
description: string | null
|
||||
state: string
|
||||
html_url: string
|
||||
due_on: string | null
|
||||
open_issues: number
|
||||
closed_issues: number
|
||||
created_at: string
|
||||
updated_at: string
|
||||
closed_at: string | null
|
||||
creator: { login: string }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const getMilestoneTool: ToolConfig<GetMilestoneParams, GetMilestoneResponse> = {
|
||||
id: 'github_get_milestone',
|
||||
name: 'GitHub Get Milestone',
|
||||
description: 'Get a specific milestone by number',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
milestone_number: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Milestone number',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/milestones/${params.milestone_number}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const progress =
|
||||
data.open_issues + data.closed_issues > 0
|
||||
? Math.round((data.closed_issues / (data.open_issues + data.closed_issues)) * 100)
|
||||
: 0
|
||||
|
||||
const content = `Milestone: ${data.title} (#${data.number})
|
||||
State: ${data.state} | Progress: ${progress}% (${data.closed_issues}/${data.open_issues + data.closed_issues} issues)
|
||||
Due: ${data.due_on ?? 'No due date'}
|
||||
Description: ${data.description ?? 'No description'}
|
||||
${data.html_url}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
number: data.number,
|
||||
title: data.title,
|
||||
description: data.description ?? null,
|
||||
state: data.state,
|
||||
html_url: data.html_url,
|
||||
due_on: data.due_on ?? null,
|
||||
open_issues: data.open_issues,
|
||||
closed_issues: data.closed_issues,
|
||||
created_at: data.created_at,
|
||||
updated_at: data.updated_at,
|
||||
closed_at: data.closed_at ?? null,
|
||||
creator: { login: data.creator?.login ?? 'unknown' },
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable milestone details' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Milestone metadata',
|
||||
properties: {
|
||||
number: { type: 'number', description: 'Milestone number' },
|
||||
title: { type: 'string', description: 'Title' },
|
||||
description: { type: 'string', description: 'Description', optional: true },
|
||||
state: { type: 'string', description: 'State' },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
due_on: { type: 'string', description: 'Due date', optional: true },
|
||||
open_issues: { type: 'number', description: 'Open issues count' },
|
||||
closed_issues: { type: 'number', description: 'Closed issues count' },
|
||||
created_at: { type: 'string', description: 'Creation date' },
|
||||
updated_at: { type: 'string', description: 'Update date' },
|
||||
closed_at: { type: 'string', description: 'Close date', optional: true },
|
||||
creator: { type: 'object', description: 'Creator info' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const getMilestoneV2Tool: ToolConfig<GetMilestoneParams, any> = {
|
||||
id: 'github_get_milestone_v2',
|
||||
name: getMilestoneTool.name,
|
||||
description: getMilestoneTool.description,
|
||||
version: '2.0.0',
|
||||
params: getMilestoneTool.params,
|
||||
request: getMilestoneTool.request,
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
...data,
|
||||
description: data.description ?? null,
|
||||
due_on: data.due_on ?? null,
|
||||
closed_at: data.closed_at ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
number: { type: 'number', description: 'Milestone number' },
|
||||
title: { type: 'string', description: 'Title' },
|
||||
description: { type: 'string', description: 'Description', optional: true },
|
||||
state: { type: 'string', description: 'State' },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
due_on: { type: 'string', description: 'Due date', optional: true },
|
||||
open_issues: { type: 'number', description: 'Open issues' },
|
||||
closed_issues: { type: 'number', description: 'Closed issues' },
|
||||
closed_at: { type: 'string', description: 'Close date', optional: true },
|
||||
creator: { type: 'object', description: 'Creator' },
|
||||
},
|
||||
}
|
||||
@@ -1,27 +1,54 @@
|
||||
import { addAssigneesTool, addAssigneesV2Tool } from '@/tools/github/add_assignees'
|
||||
import { addLabelsTool, addLabelsV2Tool } from '@/tools/github/add_labels'
|
||||
import { cancelWorkflowRunTool, cancelWorkflowRunV2Tool } from '@/tools/github/cancel_workflow_run'
|
||||
import { checkStarTool, checkStarV2Tool } from '@/tools/github/check_star'
|
||||
import { closeIssueTool, closeIssueV2Tool } from '@/tools/github/close_issue'
|
||||
import { closePRTool, closePRV2Tool } from '@/tools/github/close_pr'
|
||||
import { commentTool, commentV2Tool } from '@/tools/github/comment'
|
||||
import { compareCommitsTool, compareCommitsV2Tool } from '@/tools/github/compare_commits'
|
||||
import { createBranchTool, createBranchV2Tool } from '@/tools/github/create_branch'
|
||||
import {
|
||||
createCommentReactionTool,
|
||||
createCommentReactionV2Tool,
|
||||
} from '@/tools/github/create_comment_reaction'
|
||||
import { createFileTool, createFileV2Tool } from '@/tools/github/create_file'
|
||||
import { createGistTool, createGistV2Tool } from '@/tools/github/create_gist'
|
||||
import { createIssueTool, createIssueV2Tool } from '@/tools/github/create_issue'
|
||||
import {
|
||||
createIssueReactionTool,
|
||||
createIssueReactionV2Tool,
|
||||
} from '@/tools/github/create_issue_reaction'
|
||||
import { createMilestoneTool, createMilestoneV2Tool } from '@/tools/github/create_milestone'
|
||||
import { createPRTool, createPRV2Tool } from '@/tools/github/create_pr'
|
||||
import { createProjectTool, createProjectV2Tool } from '@/tools/github/create_project'
|
||||
import { createReleaseTool, createReleaseV2Tool } from '@/tools/github/create_release'
|
||||
import { deleteBranchTool, deleteBranchV2Tool } from '@/tools/github/delete_branch'
|
||||
import { deleteCommentTool, deleteCommentV2Tool } from '@/tools/github/delete_comment'
|
||||
import {
|
||||
deleteCommentReactionTool,
|
||||
deleteCommentReactionV2Tool,
|
||||
} from '@/tools/github/delete_comment_reaction'
|
||||
import { deleteFileTool, deleteFileV2Tool } from '@/tools/github/delete_file'
|
||||
import { deleteGistTool, deleteGistV2Tool } from '@/tools/github/delete_gist'
|
||||
import {
|
||||
deleteIssueReactionTool,
|
||||
deleteIssueReactionV2Tool,
|
||||
} from '@/tools/github/delete_issue_reaction'
|
||||
import { deleteMilestoneTool, deleteMilestoneV2Tool } from '@/tools/github/delete_milestone'
|
||||
import { deleteProjectTool, deleteProjectV2Tool } from '@/tools/github/delete_project'
|
||||
import { deleteReleaseTool, deleteReleaseV2Tool } from '@/tools/github/delete_release'
|
||||
import { forkGistTool, forkGistV2Tool } from '@/tools/github/fork_gist'
|
||||
import { forkRepoTool, forkRepoV2Tool } from '@/tools/github/fork_repo'
|
||||
import { getBranchTool, getBranchV2Tool } from '@/tools/github/get_branch'
|
||||
import {
|
||||
getBranchProtectionTool,
|
||||
getBranchProtectionV2Tool,
|
||||
} from '@/tools/github/get_branch_protection'
|
||||
import { getCommitTool, getCommitV2Tool } from '@/tools/github/get_commit'
|
||||
import { getFileContentTool, getFileContentV2Tool } from '@/tools/github/get_file_content'
|
||||
import { getGistTool, getGistV2Tool } from '@/tools/github/get_gist'
|
||||
import { getIssueTool, getIssueV2Tool } from '@/tools/github/get_issue'
|
||||
import { getMilestoneTool, getMilestoneV2Tool } from '@/tools/github/get_milestone'
|
||||
import { getPRFilesTool, getPRFilesV2Tool } from '@/tools/github/get_pr_files'
|
||||
import { getProjectTool, getProjectV2Tool } from '@/tools/github/get_project'
|
||||
import { getReleaseTool, getReleaseV2Tool } from '@/tools/github/get_release'
|
||||
@@ -31,12 +58,17 @@ import { getWorkflowRunTool, getWorkflowRunV2Tool } from '@/tools/github/get_wor
|
||||
import { issueCommentTool, issueCommentV2Tool } from '@/tools/github/issue_comment'
|
||||
import { latestCommitTool, latestCommitV2Tool } from '@/tools/github/latest_commit'
|
||||
import { listBranchesTool, listBranchesV2Tool } from '@/tools/github/list_branches'
|
||||
import { listCommitsTool, listCommitsV2Tool } from '@/tools/github/list_commits'
|
||||
import { listForksTool, listForksV2Tool } from '@/tools/github/list_forks'
|
||||
import { listGistsTool, listGistsV2Tool } from '@/tools/github/list_gists'
|
||||
import { listIssueCommentsTool, listIssueCommentsV2Tool } from '@/tools/github/list_issue_comments'
|
||||
import { listIssuesTool, listIssuesV2Tool } from '@/tools/github/list_issues'
|
||||
import { listMilestonesTool, listMilestonesV2Tool } from '@/tools/github/list_milestones'
|
||||
import { listPRCommentsTool, listPRCommentsV2Tool } from '@/tools/github/list_pr_comments'
|
||||
import { listProjectsTool, listProjectsV2Tool } from '@/tools/github/list_projects'
|
||||
import { listPRsTool, listPRsV2Tool } from '@/tools/github/list_prs'
|
||||
import { listReleasesTool, listReleasesV2Tool } from '@/tools/github/list_releases'
|
||||
import { listStargazersTool, listStargazersV2Tool } from '@/tools/github/list_stargazers'
|
||||
import { listWorkflowRunsTool, listWorkflowRunsV2Tool } from '@/tools/github/list_workflow_runs'
|
||||
import { listWorkflowsTool, listWorkflowsV2Tool } from '@/tools/github/list_workflows'
|
||||
import { mergePRTool, mergePRV2Tool } from '@/tools/github/merge_pr'
|
||||
@@ -45,20 +77,38 @@ import { removeLabelTool, removeLabelV2Tool } from '@/tools/github/remove_label'
|
||||
import { repoInfoTool, repoInfoV2Tool } from '@/tools/github/repo_info'
|
||||
import { requestReviewersTool, requestReviewersV2Tool } from '@/tools/github/request_reviewers'
|
||||
import { rerunWorkflowTool, rerunWorkflowV2Tool } from '@/tools/github/rerun_workflow'
|
||||
import { searchCodeTool, searchCodeV2Tool } from '@/tools/github/search_code'
|
||||
import { searchCommitsTool, searchCommitsV2Tool } from '@/tools/github/search_commits'
|
||||
import { searchIssuesTool, searchIssuesV2Tool } from '@/tools/github/search_issues'
|
||||
import { searchReposTool, searchReposV2Tool } from '@/tools/github/search_repos'
|
||||
import { searchUsersTool, searchUsersV2Tool } from '@/tools/github/search_users'
|
||||
import { starGistTool, starGistV2Tool } from '@/tools/github/star_gist'
|
||||
import { starRepoTool, starRepoV2Tool } from '@/tools/github/star_repo'
|
||||
import { triggerWorkflowTool, triggerWorkflowV2Tool } from '@/tools/github/trigger_workflow'
|
||||
import { unstarGistTool, unstarGistV2Tool } from '@/tools/github/unstar_gist'
|
||||
import { unstarRepoTool, unstarRepoV2Tool } from '@/tools/github/unstar_repo'
|
||||
import {
|
||||
updateBranchProtectionTool,
|
||||
updateBranchProtectionV2Tool,
|
||||
} from '@/tools/github/update_branch_protection'
|
||||
import { updateCommentTool, updateCommentV2Tool } from '@/tools/github/update_comment'
|
||||
import { updateFileTool, updateFileV2Tool } from '@/tools/github/update_file'
|
||||
import { updateGistTool, updateGistV2Tool } from '@/tools/github/update_gist'
|
||||
import { updateIssueTool, updateIssueV2Tool } from '@/tools/github/update_issue'
|
||||
import { updateMilestoneTool, updateMilestoneV2Tool } from '@/tools/github/update_milestone'
|
||||
import { updatePRTool, updatePRV2Tool } from '@/tools/github/update_pr'
|
||||
import { updateProjectTool, updateProjectV2Tool } from '@/tools/github/update_project'
|
||||
import { updateReleaseTool, updateReleaseV2Tool } from '@/tools/github/update_release'
|
||||
|
||||
// Existing exports
|
||||
export const githubAddAssigneesTool = addAssigneesTool
|
||||
export const githubAddAssigneesV2Tool = addAssigneesV2Tool
|
||||
export const githubAddLabelsTool = addLabelsTool
|
||||
export const githubAddLabelsV2Tool = addLabelsV2Tool
|
||||
export const githubCancelWorkflowRunTool = cancelWorkflowRunTool
|
||||
export const githubCancelWorkflowRunV2Tool = cancelWorkflowRunV2Tool
|
||||
export const githubCloseIssueTool = closeIssueTool
|
||||
export const githubCloseIssueV2Tool = closeIssueV2Tool
|
||||
export const githubClosePRTool = closePRTool
|
||||
export const githubClosePRV2Tool = closePRV2Tool
|
||||
export const githubCommentTool = commentTool
|
||||
@@ -67,6 +117,8 @@ export const githubCreateBranchTool = createBranchTool
|
||||
export const githubCreateBranchV2Tool = createBranchV2Tool
|
||||
export const githubCreateFileTool = createFileTool
|
||||
export const githubCreateFileV2Tool = createFileV2Tool
|
||||
export const githubCreateIssueTool = createIssueTool
|
||||
export const githubCreateIssueV2Tool = createIssueV2Tool
|
||||
export const githubCreatePRTool = createPRTool
|
||||
export const githubCreatePRV2Tool = createPRV2Tool
|
||||
export const githubCreateProjectTool = createProjectTool
|
||||
@@ -89,6 +141,8 @@ export const githubGetBranchProtectionTool = getBranchProtectionTool
|
||||
export const githubGetBranchProtectionV2Tool = getBranchProtectionV2Tool
|
||||
export const githubGetFileContentTool = getFileContentTool
|
||||
export const githubGetFileContentV2Tool = getFileContentV2Tool
|
||||
export const githubGetIssueTool = getIssueTool
|
||||
export const githubGetIssueV2Tool = getIssueV2Tool
|
||||
export const githubGetPRFilesTool = getPRFilesTool
|
||||
export const githubGetPRFilesV2Tool = getPRFilesV2Tool
|
||||
export const githubGetProjectTool = getProjectTool
|
||||
@@ -109,6 +163,8 @@ export const githubListBranchesTool = listBranchesTool
|
||||
export const githubListBranchesV2Tool = listBranchesV2Tool
|
||||
export const githubListIssueCommentsTool = listIssueCommentsTool
|
||||
export const githubListIssueCommentsV2Tool = listIssueCommentsV2Tool
|
||||
export const githubListIssuesTool = listIssuesTool
|
||||
export const githubListIssuesV2Tool = listIssuesV2Tool
|
||||
export const githubListPRCommentsTool = listPRCommentsTool
|
||||
export const githubListPRCommentsV2Tool = listPRCommentsV2Tool
|
||||
export const githubListPRsTool = listPRsTool
|
||||
@@ -125,6 +181,8 @@ export const githubMergePRTool = mergePRTool
|
||||
export const githubMergePRV2Tool = mergePRV2Tool
|
||||
export const githubPrTool = prTool
|
||||
export const githubPrV2Tool = prV2Tool
|
||||
export const githubRemoveLabelTool = removeLabelTool
|
||||
export const githubRemoveLabelV2Tool = removeLabelV2Tool
|
||||
export const githubRepoInfoTool = repoInfoTool
|
||||
export const githubRepoInfoV2Tool = repoInfoV2Tool
|
||||
export const githubRequestReviewersTool = requestReviewersTool
|
||||
@@ -139,25 +197,87 @@ export const githubUpdateCommentTool = updateCommentTool
|
||||
export const githubUpdateCommentV2Tool = updateCommentV2Tool
|
||||
export const githubUpdateFileTool = updateFileTool
|
||||
export const githubUpdateFileV2Tool = updateFileV2Tool
|
||||
export const githubUpdateIssueTool = updateIssueTool
|
||||
export const githubUpdateIssueV2Tool = updateIssueV2Tool
|
||||
export const githubUpdatePRTool = updatePRTool
|
||||
export const githubUpdatePRV2Tool = updatePRV2Tool
|
||||
export const githubUpdateProjectTool = updateProjectTool
|
||||
export const githubUpdateProjectV2Tool = updateProjectV2Tool
|
||||
export const githubUpdateReleaseTool = updateReleaseTool
|
||||
export const githubUpdateReleaseV2Tool = updateReleaseV2Tool
|
||||
export const githubAddAssigneesTool = addAssigneesTool
|
||||
export const githubAddAssigneesV2Tool = addAssigneesV2Tool
|
||||
export const githubAddLabelsTool = addLabelsTool
|
||||
export const githubAddLabelsV2Tool = addLabelsV2Tool
|
||||
export const githubCloseIssueTool = closeIssueTool
|
||||
export const githubCloseIssueV2Tool = closeIssueV2Tool
|
||||
export const githubCreateIssueTool = createIssueTool
|
||||
export const githubCreateIssueV2Tool = createIssueV2Tool
|
||||
export const githubGetIssueTool = getIssueTool
|
||||
export const githubGetIssueV2Tool = getIssueV2Tool
|
||||
export const githubListIssuesTool = listIssuesTool
|
||||
export const githubListIssuesV2Tool = listIssuesV2Tool
|
||||
export const githubRemoveLabelTool = removeLabelTool
|
||||
export const githubRemoveLabelV2Tool = removeLabelV2Tool
|
||||
export const githubUpdateIssueTool = updateIssueTool
|
||||
export const githubUpdateIssueV2Tool = updateIssueV2Tool
|
||||
|
||||
// New exports - Search tools
|
||||
export const githubSearchCodeTool = searchCodeTool
|
||||
export const githubSearchCodeV2Tool = searchCodeV2Tool
|
||||
export const githubSearchCommitsTool = searchCommitsTool
|
||||
export const githubSearchCommitsV2Tool = searchCommitsV2Tool
|
||||
export const githubSearchIssuesTool = searchIssuesTool
|
||||
export const githubSearchIssuesV2Tool = searchIssuesV2Tool
|
||||
export const githubSearchReposTool = searchReposTool
|
||||
export const githubSearchReposV2Tool = searchReposV2Tool
|
||||
export const githubSearchUsersTool = searchUsersTool
|
||||
export const githubSearchUsersV2Tool = searchUsersV2Tool
|
||||
|
||||
// New exports - Commit tools
|
||||
export const githubListCommitsTool = listCommitsTool
|
||||
export const githubListCommitsV2Tool = listCommitsV2Tool
|
||||
export const githubGetCommitTool = getCommitTool
|
||||
export const githubGetCommitV2Tool = getCommitV2Tool
|
||||
export const githubCompareCommitsTool = compareCommitsTool
|
||||
export const githubCompareCommitsV2Tool = compareCommitsV2Tool
|
||||
|
||||
// New exports - Gist tools
|
||||
export const githubCreateGistTool = createGistTool
|
||||
export const githubCreateGistV2Tool = createGistV2Tool
|
||||
export const githubGetGistTool = getGistTool
|
||||
export const githubGetGistV2Tool = getGistV2Tool
|
||||
export const githubListGistsTool = listGistsTool
|
||||
export const githubListGistsV2Tool = listGistsV2Tool
|
||||
export const githubUpdateGistTool = updateGistTool
|
||||
export const githubUpdateGistV2Tool = updateGistV2Tool
|
||||
export const githubDeleteGistTool = deleteGistTool
|
||||
export const githubDeleteGistV2Tool = deleteGistV2Tool
|
||||
export const githubForkGistTool = forkGistTool
|
||||
export const githubForkGistV2Tool = forkGistV2Tool
|
||||
export const githubStarGistTool = starGistTool
|
||||
export const githubStarGistV2Tool = starGistV2Tool
|
||||
export const githubUnstarGistTool = unstarGistTool
|
||||
export const githubUnstarGistV2Tool = unstarGistV2Tool
|
||||
|
||||
// New exports - Fork tools
|
||||
export const githubForkRepoTool = forkRepoTool
|
||||
export const githubForkRepoV2Tool = forkRepoV2Tool
|
||||
export const githubListForksTool = listForksTool
|
||||
export const githubListForksV2Tool = listForksV2Tool
|
||||
|
||||
// New exports - Milestone tools
|
||||
export const githubCreateMilestoneTool = createMilestoneTool
|
||||
export const githubCreateMilestoneV2Tool = createMilestoneV2Tool
|
||||
export const githubGetMilestoneTool = getMilestoneTool
|
||||
export const githubGetMilestoneV2Tool = getMilestoneV2Tool
|
||||
export const githubListMilestonesTool = listMilestonesTool
|
||||
export const githubListMilestonesV2Tool = listMilestonesV2Tool
|
||||
export const githubUpdateMilestoneTool = updateMilestoneTool
|
||||
export const githubUpdateMilestoneV2Tool = updateMilestoneV2Tool
|
||||
export const githubDeleteMilestoneTool = deleteMilestoneTool
|
||||
export const githubDeleteMilestoneV2Tool = deleteMilestoneV2Tool
|
||||
|
||||
// New exports - Reaction tools
|
||||
export const githubCreateIssueReactionTool = createIssueReactionTool
|
||||
export const githubCreateIssueReactionV2Tool = createIssueReactionV2Tool
|
||||
export const githubDeleteIssueReactionTool = deleteIssueReactionTool
|
||||
export const githubDeleteIssueReactionV2Tool = deleteIssueReactionV2Tool
|
||||
export const githubCreateCommentReactionTool = createCommentReactionTool
|
||||
export const githubCreateCommentReactionV2Tool = createCommentReactionV2Tool
|
||||
export const githubDeleteCommentReactionTool = deleteCommentReactionTool
|
||||
export const githubDeleteCommentReactionV2Tool = deleteCommentReactionV2Tool
|
||||
|
||||
// New exports - Star tools
|
||||
export const githubStarRepoTool = starRepoTool
|
||||
export const githubStarRepoV2Tool = starRepoV2Tool
|
||||
export const githubUnstarRepoTool = unstarRepoTool
|
||||
export const githubUnstarRepoV2Tool = unstarRepoV2Tool
|
||||
export const githubCheckStarTool = checkStarTool
|
||||
export const githubCheckStarV2Tool = checkStarV2Tool
|
||||
export const githubListStargazersTool = listStargazersTool
|
||||
export const githubListStargazersV2Tool = listStargazersV2Tool
|
||||
|
||||
243
apps/sim/tools/github/list_commits.ts
Normal file
243
apps/sim/tools/github/list_commits.ts
Normal file
@@ -0,0 +1,243 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface ListCommitsParams {
|
||||
owner: string
|
||||
repo: string
|
||||
sha?: string
|
||||
path?: string
|
||||
author?: string
|
||||
committer?: string
|
||||
since?: string
|
||||
until?: string
|
||||
per_page?: number
|
||||
page?: number
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface ListCommitsResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
commits: Array<{
|
||||
sha: string
|
||||
html_url: string
|
||||
message: string
|
||||
author: { name: string; email: string; date: string; login?: string }
|
||||
committer: { name: string; email: string; date: string; login?: string }
|
||||
}>
|
||||
count: number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const listCommitsTool: ToolConfig<ListCommitsParams, ListCommitsResponse> = {
|
||||
id: 'github_list_commits',
|
||||
name: 'GitHub List Commits',
|
||||
description:
|
||||
'List commits in a repository with optional filtering by SHA, path, author, committer, or date range',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
sha: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'SHA or branch to start listing commits from',
|
||||
},
|
||||
path: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only commits containing this file path',
|
||||
},
|
||||
author: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'GitHub login or email address to filter by author',
|
||||
},
|
||||
committer: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'GitHub login or email address to filter by committer',
|
||||
},
|
||||
since: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only commits after this date (ISO 8601 format)',
|
||||
},
|
||||
until: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only commits before this date (ISO 8601 format)',
|
||||
},
|
||||
per_page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Results per page (max 100, default: 30)',
|
||||
default: 30,
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number (default: 1)',
|
||||
default: 1,
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`https://api.github.com/repos/${params.owner}/${params.repo}/commits`)
|
||||
if (params.sha) url.searchParams.append('sha', params.sha)
|
||||
if (params.path) url.searchParams.append('path', params.path)
|
||||
if (params.author) url.searchParams.append('author', params.author)
|
||||
if (params.committer) url.searchParams.append('committer', params.committer)
|
||||
if (params.since) url.searchParams.append('since', params.since)
|
||||
if (params.until) url.searchParams.append('until', params.until)
|
||||
if (params.per_page) url.searchParams.append('per_page', String(params.per_page))
|
||||
if (params.page) url.searchParams.append('page', String(params.page))
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const commits = data.map((item: any) => ({
|
||||
sha: item.sha,
|
||||
html_url: item.html_url,
|
||||
message: item.commit.message,
|
||||
author: {
|
||||
name: item.commit.author.name,
|
||||
email: item.commit.author.email,
|
||||
date: item.commit.author.date,
|
||||
login: item.author?.login,
|
||||
},
|
||||
committer: {
|
||||
name: item.commit.committer.name,
|
||||
email: item.commit.committer.email,
|
||||
date: item.commit.committer.date,
|
||||
login: item.committer?.login,
|
||||
},
|
||||
}))
|
||||
|
||||
const content = `Found ${commits.length} commit(s):
|
||||
${commits
|
||||
.map(
|
||||
(c: any) =>
|
||||
`${c.sha.substring(0, 7)} - ${c.message.split('\n')[0]}
|
||||
Author: ${c.author.login ?? c.author.name} (${c.author.date})
|
||||
${c.html_url}`
|
||||
)
|
||||
.join('\n\n')}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
commits,
|
||||
count: commits.length,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable commit list' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Commits metadata',
|
||||
properties: {
|
||||
commits: {
|
||||
type: 'array',
|
||||
description: 'Array of commits',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
sha: { type: 'string', description: 'Commit SHA' },
|
||||
html_url: { type: 'string', description: 'GitHub web URL' },
|
||||
message: { type: 'string', description: 'Commit message' },
|
||||
author: { type: 'object', description: 'Author info' },
|
||||
committer: { type: 'object', description: 'Committer info' },
|
||||
},
|
||||
},
|
||||
},
|
||||
count: { type: 'number', description: 'Number of commits returned' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const listCommitsV2Tool: ToolConfig<ListCommitsParams, any> = {
|
||||
id: 'github_list_commits_v2',
|
||||
name: listCommitsTool.name,
|
||||
description: listCommitsTool.description,
|
||||
version: '2.0.0',
|
||||
params: listCommitsTool.params,
|
||||
request: listCommitsTool.request,
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
items: data.map((item: any) => ({
|
||||
...item,
|
||||
author: item.author ?? null,
|
||||
committer: item.committer ?? null,
|
||||
})),
|
||||
count: data.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Array of commit objects from GitHub API',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
sha: { type: 'string', description: 'Commit SHA' },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
commit: { type: 'object', description: 'Commit data' },
|
||||
author: { type: 'object', description: 'GitHub user', optional: true },
|
||||
committer: { type: 'object', description: 'GitHub user', optional: true },
|
||||
parents: { type: 'array', description: 'Parent commits' },
|
||||
},
|
||||
},
|
||||
},
|
||||
count: { type: 'number', description: 'Number of commits returned' },
|
||||
},
|
||||
}
|
||||
201
apps/sim/tools/github/list_forks.ts
Normal file
201
apps/sim/tools/github/list_forks.ts
Normal file
@@ -0,0 +1,201 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface ListForksParams {
|
||||
owner: string
|
||||
repo: string
|
||||
sort?: 'newest' | 'oldest' | 'stargazers' | 'watchers'
|
||||
per_page?: number
|
||||
page?: number
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface ListForksResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
forks: Array<{
|
||||
id: number
|
||||
full_name: string
|
||||
html_url: string
|
||||
owner: { login: string }
|
||||
stargazers_count: number
|
||||
forks_count: number
|
||||
created_at: string
|
||||
updated_at: string
|
||||
default_branch: string
|
||||
}>
|
||||
count: number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const listForksTool: ToolConfig<ListForksParams, ListForksResponse> = {
|
||||
id: 'github_list_forks',
|
||||
name: 'GitHub List Forks',
|
||||
description: 'List forks of a repository',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
sort: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort by: newest, oldest, stargazers, watchers (default: newest)',
|
||||
default: 'newest',
|
||||
},
|
||||
per_page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Results per page (max 100, default: 30)',
|
||||
default: 30,
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number (default: 1)',
|
||||
default: 1,
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`https://api.github.com/repos/${params.owner}/${params.repo}/forks`)
|
||||
if (params.sort) url.searchParams.append('sort', params.sort)
|
||||
if (params.per_page) url.searchParams.append('per_page', String(params.per_page))
|
||||
if (params.page) url.searchParams.append('page', String(params.page))
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const forks = data.map((f: any) => ({
|
||||
id: f.id,
|
||||
full_name: f.full_name,
|
||||
html_url: f.html_url,
|
||||
owner: { login: f.owner?.login ?? 'unknown' },
|
||||
stargazers_count: f.stargazers_count,
|
||||
forks_count: f.forks_count,
|
||||
created_at: f.created_at,
|
||||
updated_at: f.updated_at,
|
||||
default_branch: f.default_branch,
|
||||
}))
|
||||
|
||||
const content = `Found ${forks.length} fork(s):
|
||||
${forks
|
||||
.map(
|
||||
(f: any) =>
|
||||
`${f.full_name} ⭐ ${f.stargazers_count}
|
||||
Owner: ${f.owner.login}
|
||||
${f.html_url}`
|
||||
)
|
||||
.join('\n\n')}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
forks,
|
||||
count: forks.length,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable fork list' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Forks metadata',
|
||||
properties: {
|
||||
forks: {
|
||||
type: 'array',
|
||||
description: 'Array of forks',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'Repository ID' },
|
||||
full_name: { type: 'string', description: 'Full name' },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
owner: { type: 'object', description: 'Owner info' },
|
||||
stargazers_count: { type: 'number', description: 'Star count' },
|
||||
forks_count: { type: 'number', description: 'Fork count' },
|
||||
created_at: { type: 'string', description: 'Creation date' },
|
||||
updated_at: { type: 'string', description: 'Update date' },
|
||||
default_branch: { type: 'string', description: 'Default branch' },
|
||||
},
|
||||
},
|
||||
},
|
||||
count: { type: 'number', description: 'Number of forks returned' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const listForksV2Tool: ToolConfig<ListForksParams, any> = {
|
||||
id: 'github_list_forks_v2',
|
||||
name: listForksTool.name,
|
||||
description: listForksTool.description,
|
||||
version: '2.0.0',
|
||||
params: listForksTool.params,
|
||||
request: listForksTool.request,
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
items: data,
|
||||
count: data.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Array of fork repository objects from GitHub API',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'Repository ID' },
|
||||
full_name: { type: 'string', description: 'Full name' },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
owner: { type: 'object', description: 'Owner' },
|
||||
stargazers_count: { type: 'number', description: 'Stars' },
|
||||
forks_count: { type: 'number', description: 'Forks' },
|
||||
},
|
||||
},
|
||||
},
|
||||
count: { type: 'number', description: 'Number of forks returned' },
|
||||
},
|
||||
}
|
||||
201
apps/sim/tools/github/list_gists.ts
Normal file
201
apps/sim/tools/github/list_gists.ts
Normal file
@@ -0,0 +1,201 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface ListGistsParams {
|
||||
username?: string
|
||||
since?: string
|
||||
per_page?: number
|
||||
page?: number
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface ListGistsResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
gists: Array<{
|
||||
id: string
|
||||
html_url: string
|
||||
description: string | null
|
||||
public: boolean
|
||||
created_at: string
|
||||
updated_at: string
|
||||
files: string[]
|
||||
owner: { login: string }
|
||||
comments: number
|
||||
}>
|
||||
count: number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const listGistsTool: ToolConfig<ListGistsParams, ListGistsResponse> = {
|
||||
id: 'github_list_gists',
|
||||
name: 'GitHub List Gists',
|
||||
description: 'List gists for a user or the authenticated user',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
username: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: "GitHub username (omit for authenticated user's gists)",
|
||||
},
|
||||
since: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only gists updated after this time (ISO 8601)',
|
||||
},
|
||||
per_page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Results per page (max 100, default: 30)',
|
||||
default: 30,
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number (default: 1)',
|
||||
default: 1,
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = params.username
|
||||
? `https://api.github.com/users/${params.username}/gists`
|
||||
: 'https://api.github.com/gists'
|
||||
const url = new URL(baseUrl)
|
||||
if (params.since) url.searchParams.append('since', params.since)
|
||||
if (params.per_page) url.searchParams.append('per_page', String(params.per_page))
|
||||
if (params.page) url.searchParams.append('page', String(params.page))
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const gists = data.map((g: any) => ({
|
||||
id: g.id,
|
||||
html_url: g.html_url,
|
||||
description: g.description ?? null,
|
||||
public: g.public,
|
||||
created_at: g.created_at,
|
||||
updated_at: g.updated_at,
|
||||
files: Object.keys(g.files ?? {}),
|
||||
owner: { login: g.owner?.login ?? 'unknown' },
|
||||
comments: g.comments ?? 0,
|
||||
}))
|
||||
|
||||
const content = `Found ${gists.length} gist(s):
|
||||
${gists
|
||||
.map(
|
||||
(g: any) =>
|
||||
`${g.id} - ${g.description ?? 'No description'} (${g.public ? 'public' : 'secret'})
|
||||
Files: ${g.files.join(', ')}
|
||||
${g.html_url}`
|
||||
)
|
||||
.join('\n\n')}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
gists,
|
||||
count: gists.length,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable gist list' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Gists metadata',
|
||||
properties: {
|
||||
gists: {
|
||||
type: 'array',
|
||||
description: 'Array of gists',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Gist ID' },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
description: { type: 'string', description: 'Description', optional: true },
|
||||
public: { type: 'boolean', description: 'Is public' },
|
||||
created_at: { type: 'string', description: 'Creation date' },
|
||||
updated_at: { type: 'string', description: 'Update date' },
|
||||
files: { type: 'array', description: 'File names' },
|
||||
owner: { type: 'object', description: 'Owner info' },
|
||||
comments: { type: 'number', description: 'Comment count' },
|
||||
},
|
||||
},
|
||||
},
|
||||
count: { type: 'number', description: 'Number of gists returned' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const listGistsV2Tool: ToolConfig<ListGistsParams, any> = {
|
||||
id: 'github_list_gists_v2',
|
||||
name: listGistsTool.name,
|
||||
description: listGistsTool.description,
|
||||
version: '2.0.0',
|
||||
params: listGistsTool.params,
|
||||
request: listGistsTool.request,
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
items: data.map((g: any) => ({
|
||||
...g,
|
||||
description: g.description ?? null,
|
||||
files: g.files ?? {},
|
||||
comments: g.comments ?? 0,
|
||||
})),
|
||||
count: data.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Array of gist objects from GitHub API',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Gist ID' },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
description: { type: 'string', description: 'Description', optional: true },
|
||||
public: { type: 'boolean', description: 'Is public' },
|
||||
files: { type: 'object', description: 'Files' },
|
||||
owner: { type: 'object', description: 'Owner' },
|
||||
},
|
||||
},
|
||||
},
|
||||
count: { type: 'number', description: 'Number of gists returned' },
|
||||
},
|
||||
}
|
||||
226
apps/sim/tools/github/list_milestones.ts
Normal file
226
apps/sim/tools/github/list_milestones.ts
Normal file
@@ -0,0 +1,226 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface ListMilestonesParams {
|
||||
owner: string
|
||||
repo: string
|
||||
state?: 'open' | 'closed' | 'all'
|
||||
sort?: 'due_on' | 'completeness'
|
||||
direction?: 'asc' | 'desc'
|
||||
per_page?: number
|
||||
page?: number
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface ListMilestonesResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
milestones: Array<{
|
||||
number: number
|
||||
title: string
|
||||
description: string | null
|
||||
state: string
|
||||
html_url: string
|
||||
due_on: string | null
|
||||
open_issues: number
|
||||
closed_issues: number
|
||||
}>
|
||||
count: number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const listMilestonesTool: ToolConfig<ListMilestonesParams, ListMilestonesResponse> = {
|
||||
id: 'github_list_milestones',
|
||||
name: 'GitHub List Milestones',
|
||||
description: 'List milestones in a repository',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
state: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by state: open, closed, all (default: open)',
|
||||
default: 'open',
|
||||
},
|
||||
sort: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort by: due_on or completeness (default: due_on)',
|
||||
default: 'due_on',
|
||||
},
|
||||
direction: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort direction: asc or desc (default: asc)',
|
||||
default: 'asc',
|
||||
},
|
||||
per_page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Results per page (max 100, default: 30)',
|
||||
default: 30,
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number (default: 1)',
|
||||
default: 1,
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`https://api.github.com/repos/${params.owner}/${params.repo}/milestones`)
|
||||
if (params.state) url.searchParams.append('state', params.state)
|
||||
if (params.sort) url.searchParams.append('sort', params.sort)
|
||||
if (params.direction) url.searchParams.append('direction', params.direction)
|
||||
if (params.per_page) url.searchParams.append('per_page', String(params.per_page))
|
||||
if (params.page) url.searchParams.append('page', String(params.page))
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const milestones = data.map((m: any) => {
|
||||
const total = m.open_issues + m.closed_issues
|
||||
const progress = total > 0 ? Math.round((m.closed_issues / total) * 100) : 0
|
||||
return {
|
||||
number: m.number,
|
||||
title: m.title,
|
||||
description: m.description ?? null,
|
||||
state: m.state,
|
||||
html_url: m.html_url,
|
||||
due_on: m.due_on ?? null,
|
||||
open_issues: m.open_issues,
|
||||
closed_issues: m.closed_issues,
|
||||
progress,
|
||||
}
|
||||
})
|
||||
|
||||
const content = `Found ${milestones.length} milestone(s):
|
||||
${milestones
|
||||
.map(
|
||||
(m: any) =>
|
||||
`#${m.number}: ${m.title} (${m.state})
|
||||
Progress: ${m.progress}% (${m.closed_issues}/${m.open_issues + m.closed_issues} issues)
|
||||
Due: ${m.due_on ?? 'No due date'}
|
||||
${m.html_url}`
|
||||
)
|
||||
.join('\n\n')}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
milestones,
|
||||
count: milestones.length,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable milestone list' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Milestones metadata',
|
||||
properties: {
|
||||
milestones: {
|
||||
type: 'array',
|
||||
description: 'Array of milestones',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
number: { type: 'number', description: 'Milestone number' },
|
||||
title: { type: 'string', description: 'Title' },
|
||||
description: { type: 'string', description: 'Description', optional: true },
|
||||
state: { type: 'string', description: 'State' },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
due_on: { type: 'string', description: 'Due date', optional: true },
|
||||
open_issues: { type: 'number', description: 'Open issues' },
|
||||
closed_issues: { type: 'number', description: 'Closed issues' },
|
||||
},
|
||||
},
|
||||
},
|
||||
count: { type: 'number', description: 'Number of milestones returned' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const listMilestonesV2Tool: ToolConfig<ListMilestonesParams, any> = {
|
||||
id: 'github_list_milestones_v2',
|
||||
name: listMilestonesTool.name,
|
||||
description: listMilestonesTool.description,
|
||||
version: '2.0.0',
|
||||
params: listMilestonesTool.params,
|
||||
request: listMilestonesTool.request,
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
items: data.map((m: any) => ({
|
||||
...m,
|
||||
description: m.description ?? null,
|
||||
due_on: m.due_on ?? null,
|
||||
})),
|
||||
count: data.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Array of milestone objects from GitHub API',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
number: { type: 'number', description: 'Milestone number' },
|
||||
title: { type: 'string', description: 'Title' },
|
||||
state: { type: 'string', description: 'State' },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
open_issues: { type: 'number', description: 'Open issues' },
|
||||
closed_issues: { type: 'number', description: 'Closed issues' },
|
||||
},
|
||||
},
|
||||
},
|
||||
count: { type: 'number', description: 'Number of milestones returned' },
|
||||
},
|
||||
}
|
||||
172
apps/sim/tools/github/list_stargazers.ts
Normal file
172
apps/sim/tools/github/list_stargazers.ts
Normal file
@@ -0,0 +1,172 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface ListStargazersParams {
|
||||
owner: string
|
||||
repo: string
|
||||
per_page?: number
|
||||
page?: number
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface ListStargazersResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
stargazers: Array<{
|
||||
login: string
|
||||
id: number
|
||||
avatar_url: string
|
||||
html_url: string
|
||||
type: string
|
||||
}>
|
||||
count: number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const listStargazersTool: ToolConfig<ListStargazersParams, ListStargazersResponse> = {
|
||||
id: 'github_list_stargazers',
|
||||
name: 'GitHub List Stargazers',
|
||||
description: 'List users who have starred a repository',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
per_page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Results per page (max 100, default: 30)',
|
||||
default: 30,
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number (default: 1)',
|
||||
default: 1,
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`https://api.github.com/repos/${params.owner}/${params.repo}/stargazers`)
|
||||
if (params.per_page) url.searchParams.append('per_page', String(params.per_page))
|
||||
if (params.page) url.searchParams.append('page', String(params.page))
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const stargazers = data.map((u: any) => ({
|
||||
login: u.login,
|
||||
id: u.id,
|
||||
avatar_url: u.avatar_url,
|
||||
html_url: u.html_url,
|
||||
type: u.type,
|
||||
}))
|
||||
|
||||
const content = `Found ${stargazers.length} stargazer(s):
|
||||
${stargazers.map((u: any) => `@${u.login} (${u.type}) - ${u.html_url}`).join('\n')}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
stargazers,
|
||||
count: stargazers.length,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable stargazer list' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Stargazers metadata',
|
||||
properties: {
|
||||
stargazers: {
|
||||
type: 'array',
|
||||
description: 'Array of stargazers',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
login: { type: 'string', description: 'Username' },
|
||||
id: { type: 'number', description: 'User ID' },
|
||||
avatar_url: { type: 'string', description: 'Avatar URL' },
|
||||
html_url: { type: 'string', description: 'Profile URL' },
|
||||
type: { type: 'string', description: 'User or Organization' },
|
||||
},
|
||||
},
|
||||
},
|
||||
count: { type: 'number', description: 'Number of stargazers returned' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const listStargazersV2Tool: ToolConfig<ListStargazersParams, any> = {
|
||||
id: 'github_list_stargazers_v2',
|
||||
name: listStargazersTool.name,
|
||||
description: listStargazersTool.description,
|
||||
version: '2.0.0',
|
||||
params: listStargazersTool.params,
|
||||
request: listStargazersTool.request,
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
items: data,
|
||||
count: data.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Array of user objects from GitHub API',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
login: { type: 'string', description: 'Username' },
|
||||
id: { type: 'number', description: 'User ID' },
|
||||
avatar_url: { type: 'string', description: 'Avatar URL' },
|
||||
html_url: { type: 'string', description: 'Profile URL' },
|
||||
type: { type: 'string', description: 'User or Organization' },
|
||||
},
|
||||
},
|
||||
},
|
||||
count: { type: 'number', description: 'Number of stargazers returned' },
|
||||
},
|
||||
}
|
||||
211
apps/sim/tools/github/search_code.ts
Normal file
211
apps/sim/tools/github/search_code.ts
Normal file
@@ -0,0 +1,211 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface SearchCodeParams {
|
||||
q: string
|
||||
sort?: 'indexed'
|
||||
order?: 'asc' | 'desc'
|
||||
per_page?: number
|
||||
page?: number
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface SearchCodeResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
total_count: number
|
||||
incomplete_results: boolean
|
||||
items: Array<{
|
||||
name: string
|
||||
path: string
|
||||
sha: string
|
||||
html_url: string
|
||||
repository: {
|
||||
full_name: string
|
||||
html_url: string
|
||||
}
|
||||
}>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const searchCodeTool: ToolConfig<SearchCodeParams, SearchCodeResponse> = {
|
||||
id: 'github_search_code',
|
||||
name: 'GitHub Search Code',
|
||||
description:
|
||||
'Search for code across GitHub repositories. Use qualifiers like repo:owner/name, language:js, path:src, extension:py',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
q: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Search query with optional qualifiers (repo:, language:, path:, extension:, user:, org:)',
|
||||
},
|
||||
sort: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort by indexed date (default: best match)',
|
||||
},
|
||||
order: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort order: asc or desc (default: desc)',
|
||||
},
|
||||
per_page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Results per page (max 100, default: 30)',
|
||||
default: 30,
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number (default: 1)',
|
||||
default: 1,
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://api.github.com/search/code')
|
||||
url.searchParams.append('q', params.q)
|
||||
if (params.sort) url.searchParams.append('sort', params.sort)
|
||||
if (params.order) url.searchParams.append('order', params.order)
|
||||
if (params.per_page) url.searchParams.append('per_page', String(params.per_page))
|
||||
if (params.page) url.searchParams.append('page', String(params.page))
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const items = data.items.map((item: any) => ({
|
||||
name: item.name,
|
||||
path: item.path,
|
||||
sha: item.sha,
|
||||
html_url: item.html_url,
|
||||
repository: {
|
||||
full_name: item.repository.full_name,
|
||||
html_url: item.repository.html_url,
|
||||
},
|
||||
}))
|
||||
|
||||
const content = `Found ${data.total_count} code result(s)${data.incomplete_results ? ' (incomplete)' : ''}:
|
||||
${items
|
||||
.map(
|
||||
(item: any) =>
|
||||
`- ${item.repository.full_name}/${item.path}
|
||||
${item.html_url}`
|
||||
)
|
||||
.join('\n')}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
total_count: data.total_count,
|
||||
incomplete_results: data.incomplete_results,
|
||||
items,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable search results' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Search results metadata',
|
||||
properties: {
|
||||
total_count: { type: 'number', description: 'Total matching results' },
|
||||
incomplete_results: { type: 'boolean', description: 'Whether results are incomplete' },
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Array of code matches',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string', description: 'File name' },
|
||||
path: { type: 'string', description: 'File path' },
|
||||
sha: { type: 'string', description: 'Blob SHA' },
|
||||
html_url: { type: 'string', description: 'GitHub web URL' },
|
||||
repository: {
|
||||
type: 'object',
|
||||
description: 'Repository info',
|
||||
properties: {
|
||||
full_name: { type: 'string', description: 'Repository full name' },
|
||||
html_url: { type: 'string', description: 'Repository URL' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const searchCodeV2Tool: ToolConfig<SearchCodeParams, any> = {
|
||||
id: 'github_search_code_v2',
|
||||
name: searchCodeTool.name,
|
||||
description: searchCodeTool.description,
|
||||
version: '2.0.0',
|
||||
params: searchCodeTool.params,
|
||||
request: searchCodeTool.request,
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
total_count: data.total_count,
|
||||
incomplete_results: data.incomplete_results,
|
||||
items: data.items.map((item: any) => ({
|
||||
...item,
|
||||
text_matches: item.text_matches ?? [],
|
||||
})),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
total_count: { type: 'number', description: 'Total matching results' },
|
||||
incomplete_results: { type: 'boolean', description: 'Whether results are incomplete' },
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Array of code matches from GitHub API',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string', description: 'File name' },
|
||||
path: { type: 'string', description: 'File path' },
|
||||
sha: { type: 'string', description: 'Blob SHA' },
|
||||
html_url: { type: 'string', description: 'GitHub web URL' },
|
||||
repository: { type: 'object', description: 'Repository object' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
224
apps/sim/tools/github/search_commits.ts
Normal file
224
apps/sim/tools/github/search_commits.ts
Normal file
@@ -0,0 +1,224 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface SearchCommitsParams {
|
||||
q: string
|
||||
sort?: 'author-date' | 'committer-date'
|
||||
order?: 'asc' | 'desc'
|
||||
per_page?: number
|
||||
page?: number
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface SearchCommitsResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
total_count: number
|
||||
incomplete_results: boolean
|
||||
items: Array<{
|
||||
sha: string
|
||||
html_url: string
|
||||
commit: {
|
||||
message: string
|
||||
author: { name: string; email: string; date: string }
|
||||
committer: { name: string; email: string; date: string }
|
||||
}
|
||||
author: { login: string } | null
|
||||
committer: { login: string } | null
|
||||
repository: { full_name: string; html_url: string }
|
||||
}>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const searchCommitsTool: ToolConfig<SearchCommitsParams, SearchCommitsResponse> = {
|
||||
id: 'github_search_commits',
|
||||
name: 'GitHub Search Commits',
|
||||
description:
|
||||
'Search for commits across GitHub. Use qualifiers like repo:owner/name, author:user, committer:user, author-date:>2023-01-01',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
q: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Search query with optional qualifiers (repo:, author:, committer:, author-date:, committer-date:, merge:true/false)',
|
||||
},
|
||||
sort: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort by: author-date or committer-date (default: best match)',
|
||||
},
|
||||
order: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort order: asc or desc (default: desc)',
|
||||
},
|
||||
per_page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Results per page (max 100, default: 30)',
|
||||
default: 30,
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number (default: 1)',
|
||||
default: 1,
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://api.github.com/search/commits')
|
||||
url.searchParams.append('q', params.q)
|
||||
if (params.sort) url.searchParams.append('sort', params.sort)
|
||||
if (params.order) url.searchParams.append('order', params.order)
|
||||
if (params.per_page) url.searchParams.append('per_page', String(params.per_page))
|
||||
if (params.page) url.searchParams.append('page', String(params.page))
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.cloak-preview+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const items = data.items.map((item: any) => ({
|
||||
sha: item.sha,
|
||||
html_url: item.html_url,
|
||||
commit: {
|
||||
message: item.commit.message,
|
||||
author: item.commit.author,
|
||||
committer: item.commit.committer,
|
||||
},
|
||||
author: item.author ? { login: item.author.login } : null,
|
||||
committer: item.committer ? { login: item.committer.login } : null,
|
||||
repository: {
|
||||
full_name: item.repository.full_name,
|
||||
html_url: item.repository.html_url,
|
||||
},
|
||||
}))
|
||||
|
||||
const content = `Found ${data.total_count} commit(s)${data.incomplete_results ? ' (incomplete)' : ''}:
|
||||
${items
|
||||
.map(
|
||||
(item: any) =>
|
||||
`${item.sha.substring(0, 7)} - ${item.commit.message.split('\n')[0]}
|
||||
Repository: ${item.repository.full_name}
|
||||
Author: ${item.author?.login ?? item.commit.author.name} (${item.commit.author.date})
|
||||
${item.html_url}`
|
||||
)
|
||||
.join('\n\n')}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
total_count: data.total_count,
|
||||
incomplete_results: data.incomplete_results,
|
||||
items,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable search results' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Search results metadata',
|
||||
properties: {
|
||||
total_count: { type: 'number', description: 'Total matching results' },
|
||||
incomplete_results: { type: 'boolean', description: 'Whether results are incomplete' },
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Array of commits',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
sha: { type: 'string', description: 'Commit SHA' },
|
||||
html_url: { type: 'string', description: 'GitHub web URL' },
|
||||
commit: {
|
||||
type: 'object',
|
||||
description: 'Commit details',
|
||||
properties: {
|
||||
message: { type: 'string', description: 'Commit message' },
|
||||
author: { type: 'object', description: 'Author info' },
|
||||
committer: { type: 'object', description: 'Committer info' },
|
||||
},
|
||||
},
|
||||
author: { type: 'object', description: 'GitHub user (author)', optional: true },
|
||||
committer: { type: 'object', description: 'GitHub user (committer)', optional: true },
|
||||
repository: { type: 'object', description: 'Repository info' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const searchCommitsV2Tool: ToolConfig<SearchCommitsParams, any> = {
|
||||
id: 'github_search_commits_v2',
|
||||
name: searchCommitsTool.name,
|
||||
description: searchCommitsTool.description,
|
||||
version: '2.0.0',
|
||||
params: searchCommitsTool.params,
|
||||
request: searchCommitsTool.request,
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
total_count: data.total_count,
|
||||
incomplete_results: data.incomplete_results,
|
||||
items: data.items.map((item: any) => ({
|
||||
...item,
|
||||
author: item.author ?? null,
|
||||
committer: item.committer ?? null,
|
||||
})),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
total_count: { type: 'number', description: 'Total matching results' },
|
||||
incomplete_results: { type: 'boolean', description: 'Whether results are incomplete' },
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Array of commit objects from GitHub API',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
sha: { type: 'string', description: 'Commit SHA' },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
commit: { type: 'object', description: 'Commit data' },
|
||||
author: { type: 'object', description: 'GitHub user', optional: true },
|
||||
committer: { type: 'object', description: 'GitHub user', optional: true },
|
||||
repository: { type: 'object', description: 'Repository' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
240
apps/sim/tools/github/search_issues.ts
Normal file
240
apps/sim/tools/github/search_issues.ts
Normal file
@@ -0,0 +1,240 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface SearchIssuesParams {
|
||||
q: string
|
||||
sort?:
|
||||
| 'comments'
|
||||
| 'reactions'
|
||||
| 'reactions-+1'
|
||||
| 'reactions--1'
|
||||
| 'reactions-smile'
|
||||
| 'reactions-thinking_face'
|
||||
| 'reactions-heart'
|
||||
| 'reactions-tada'
|
||||
| 'interactions'
|
||||
| 'created'
|
||||
| 'updated'
|
||||
order?: 'asc' | 'desc'
|
||||
per_page?: number
|
||||
page?: number
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface SearchIssuesResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
total_count: number
|
||||
incomplete_results: boolean
|
||||
items: Array<{
|
||||
number: number
|
||||
title: string
|
||||
state: string
|
||||
html_url: string
|
||||
user: { login: string }
|
||||
labels: string[]
|
||||
created_at: string
|
||||
updated_at: string
|
||||
comments: number
|
||||
is_pull_request: boolean
|
||||
repository_url: string
|
||||
}>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const searchIssuesTool: ToolConfig<SearchIssuesParams, SearchIssuesResponse> = {
|
||||
id: 'github_search_issues',
|
||||
name: 'GitHub Search Issues',
|
||||
description:
|
||||
'Search for issues and pull requests across GitHub. Use qualifiers like repo:owner/name, is:issue, is:pr, state:open, label:bug, author:user',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
q: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Search query with optional qualifiers (repo:, is:issue, is:pr, state:, label:, author:, assignee:)',
|
||||
},
|
||||
sort: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Sort by: comments, reactions, created, updated, interactions (default: best match)',
|
||||
},
|
||||
order: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort order: asc or desc (default: desc)',
|
||||
},
|
||||
per_page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Results per page (max 100, default: 30)',
|
||||
default: 30,
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number (default: 1)',
|
||||
default: 1,
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://api.github.com/search/issues')
|
||||
url.searchParams.append('q', params.q)
|
||||
if (params.sort) url.searchParams.append('sort', params.sort)
|
||||
if (params.order) url.searchParams.append('order', params.order)
|
||||
if (params.per_page) url.searchParams.append('per_page', String(params.per_page))
|
||||
if (params.page) url.searchParams.append('page', String(params.page))
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const items = data.items.map((item: any) => ({
|
||||
number: item.number,
|
||||
title: item.title,
|
||||
state: item.state,
|
||||
html_url: item.html_url,
|
||||
user: { login: item.user?.login ?? 'unknown' },
|
||||
labels: item.labels?.map((l: any) => l.name) ?? [],
|
||||
created_at: item.created_at,
|
||||
updated_at: item.updated_at,
|
||||
comments: item.comments ?? 0,
|
||||
is_pull_request: !!item.pull_request,
|
||||
repository_url: item.repository_url,
|
||||
}))
|
||||
|
||||
const content = `Found ${data.total_count} result(s)${data.incomplete_results ? ' (incomplete)' : ''}:
|
||||
${items
|
||||
.map(
|
||||
(item: any) =>
|
||||
`#${item.number}: "${item.title}" (${item.state}) [${item.is_pull_request ? 'PR' : 'Issue'}]
|
||||
${item.html_url}
|
||||
Labels: ${item.labels.length > 0 ? item.labels.join(', ') : 'none'} | Comments: ${item.comments}`
|
||||
)
|
||||
.join('\n\n')}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
total_count: data.total_count,
|
||||
incomplete_results: data.incomplete_results,
|
||||
items,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable search results' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Search results metadata',
|
||||
properties: {
|
||||
total_count: { type: 'number', description: 'Total matching results' },
|
||||
incomplete_results: { type: 'boolean', description: 'Whether results are incomplete' },
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Array of issues/PRs',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
number: { type: 'number', description: 'Issue/PR number' },
|
||||
title: { type: 'string', description: 'Title' },
|
||||
state: { type: 'string', description: 'State (open/closed)' },
|
||||
html_url: { type: 'string', description: 'GitHub web URL' },
|
||||
user: { type: 'object', description: 'Author info' },
|
||||
labels: { type: 'array', description: 'Label names' },
|
||||
created_at: { type: 'string', description: 'Creation date' },
|
||||
updated_at: { type: 'string', description: 'Last update date' },
|
||||
comments: { type: 'number', description: 'Comment count' },
|
||||
is_pull_request: { type: 'boolean', description: 'Whether this is a PR' },
|
||||
repository_url: { type: 'string', description: 'Repository API URL' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const searchIssuesV2Tool: ToolConfig<SearchIssuesParams, any> = {
|
||||
id: 'github_search_issues_v2',
|
||||
name: searchIssuesTool.name,
|
||||
description: searchIssuesTool.description,
|
||||
version: '2.0.0',
|
||||
params: searchIssuesTool.params,
|
||||
request: searchIssuesTool.request,
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
total_count: data.total_count,
|
||||
incomplete_results: data.incomplete_results,
|
||||
items: data.items.map((item: any) => ({
|
||||
...item,
|
||||
body: item.body ?? null,
|
||||
closed_at: item.closed_at ?? null,
|
||||
milestone: item.milestone ?? null,
|
||||
labels: item.labels ?? [],
|
||||
assignees: item.assignees ?? [],
|
||||
})),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
total_count: { type: 'number', description: 'Total matching results' },
|
||||
incomplete_results: { type: 'boolean', description: 'Whether results are incomplete' },
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Array of issue/PR objects from GitHub API',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'Issue ID' },
|
||||
number: { type: 'number', description: 'Issue number' },
|
||||
title: { type: 'string', description: 'Title' },
|
||||
state: { type: 'string', description: 'State' },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
body: { type: 'string', description: 'Body text', optional: true },
|
||||
user: { type: 'object', description: 'Author' },
|
||||
labels: { type: 'array', description: 'Labels' },
|
||||
assignees: { type: 'array', description: 'Assignees' },
|
||||
created_at: { type: 'string', description: 'Creation date' },
|
||||
updated_at: { type: 'string', description: 'Update date' },
|
||||
closed_at: { type: 'string', description: 'Close date', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
226
apps/sim/tools/github/search_repos.ts
Normal file
226
apps/sim/tools/github/search_repos.ts
Normal file
@@ -0,0 +1,226 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface SearchReposParams {
|
||||
q: string
|
||||
sort?: 'stars' | 'forks' | 'help-wanted-issues' | 'updated'
|
||||
order?: 'asc' | 'desc'
|
||||
per_page?: number
|
||||
page?: number
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface SearchReposResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
total_count: number
|
||||
incomplete_results: boolean
|
||||
items: Array<{
|
||||
id: number
|
||||
full_name: string
|
||||
description: string | null
|
||||
html_url: string
|
||||
stargazers_count: number
|
||||
forks_count: number
|
||||
language: string | null
|
||||
topics: string[]
|
||||
created_at: string
|
||||
updated_at: string
|
||||
owner: { login: string }
|
||||
}>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const searchReposTool: ToolConfig<SearchReposParams, SearchReposResponse> = {
|
||||
id: 'github_search_repos',
|
||||
name: 'GitHub Search Repositories',
|
||||
description:
|
||||
'Search for repositories across GitHub. Use qualifiers like language:python, stars:>1000, topic:react, user:owner, org:name',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
q: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Search query with optional qualifiers (language:, stars:, forks:, topic:, user:, org:, in:name,description,readme)',
|
||||
},
|
||||
sort: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort by: stars, forks, help-wanted-issues, updated (default: best match)',
|
||||
},
|
||||
order: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort order: asc or desc (default: desc)',
|
||||
},
|
||||
per_page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Results per page (max 100, default: 30)',
|
||||
default: 30,
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number (default: 1)',
|
||||
default: 1,
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://api.github.com/search/repositories')
|
||||
url.searchParams.append('q', params.q)
|
||||
if (params.sort) url.searchParams.append('sort', params.sort)
|
||||
if (params.order) url.searchParams.append('order', params.order)
|
||||
if (params.per_page) url.searchParams.append('per_page', String(params.per_page))
|
||||
if (params.page) url.searchParams.append('page', String(params.page))
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const items = data.items.map((item: any) => ({
|
||||
id: item.id,
|
||||
full_name: item.full_name,
|
||||
description: item.description ?? null,
|
||||
html_url: item.html_url,
|
||||
stargazers_count: item.stargazers_count,
|
||||
forks_count: item.forks_count,
|
||||
language: item.language ?? null,
|
||||
topics: item.topics ?? [],
|
||||
created_at: item.created_at,
|
||||
updated_at: item.updated_at,
|
||||
owner: { login: item.owner?.login ?? 'unknown' },
|
||||
}))
|
||||
|
||||
const content = `Found ${data.total_count} repository(s)${data.incomplete_results ? ' (incomplete)' : ''}:
|
||||
${items
|
||||
.map(
|
||||
(item: any) =>
|
||||
`${item.full_name} ⭐ ${item.stargazers_count} | 🍴 ${item.forks_count}
|
||||
${item.description ?? 'No description'}
|
||||
${item.html_url}
|
||||
Language: ${item.language ?? 'N/A'} | Topics: ${item.topics.length > 0 ? item.topics.join(', ') : 'none'}`
|
||||
)
|
||||
.join('\n\n')}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
total_count: data.total_count,
|
||||
incomplete_results: data.incomplete_results,
|
||||
items,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable search results' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Search results metadata',
|
||||
properties: {
|
||||
total_count: { type: 'number', description: 'Total matching results' },
|
||||
incomplete_results: { type: 'boolean', description: 'Whether results are incomplete' },
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Array of repositories',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'Repository ID' },
|
||||
full_name: { type: 'string', description: 'Full name (owner/repo)' },
|
||||
description: { type: 'string', description: 'Description', optional: true },
|
||||
html_url: { type: 'string', description: 'GitHub web URL' },
|
||||
stargazers_count: { type: 'number', description: 'Star count' },
|
||||
forks_count: { type: 'number', description: 'Fork count' },
|
||||
language: { type: 'string', description: 'Primary language', optional: true },
|
||||
topics: { type: 'array', description: 'Repository topics' },
|
||||
created_at: { type: 'string', description: 'Creation date' },
|
||||
updated_at: { type: 'string', description: 'Last update date' },
|
||||
owner: { type: 'object', description: 'Owner info' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const searchReposV2Tool: ToolConfig<SearchReposParams, any> = {
|
||||
id: 'github_search_repos_v2',
|
||||
name: searchReposTool.name,
|
||||
description: searchReposTool.description,
|
||||
version: '2.0.0',
|
||||
params: searchReposTool.params,
|
||||
request: searchReposTool.request,
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
total_count: data.total_count,
|
||||
incomplete_results: data.incomplete_results,
|
||||
items: data.items.map((item: any) => ({
|
||||
...item,
|
||||
description: item.description ?? null,
|
||||
language: item.language ?? null,
|
||||
topics: item.topics ?? [],
|
||||
license: item.license ?? null,
|
||||
})),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
total_count: { type: 'number', description: 'Total matching results' },
|
||||
incomplete_results: { type: 'boolean', description: 'Whether results are incomplete' },
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Array of repository objects from GitHub API',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'Repository ID' },
|
||||
full_name: { type: 'string', description: 'Full name' },
|
||||
description: { type: 'string', description: 'Description', optional: true },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
stargazers_count: { type: 'number', description: 'Stars' },
|
||||
forks_count: { type: 'number', description: 'Forks' },
|
||||
open_issues_count: { type: 'number', description: 'Open issues' },
|
||||
language: { type: 'string', description: 'Language', optional: true },
|
||||
topics: { type: 'array', description: 'Topics' },
|
||||
owner: { type: 'object', description: 'Owner' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
193
apps/sim/tools/github/search_users.ts
Normal file
193
apps/sim/tools/github/search_users.ts
Normal file
@@ -0,0 +1,193 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface SearchUsersParams {
|
||||
q: string
|
||||
sort?: 'followers' | 'repositories' | 'joined'
|
||||
order?: 'asc' | 'desc'
|
||||
per_page?: number
|
||||
page?: number
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface SearchUsersResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
total_count: number
|
||||
incomplete_results: boolean
|
||||
items: Array<{
|
||||
id: number
|
||||
login: string
|
||||
html_url: string
|
||||
avatar_url: string
|
||||
type: string
|
||||
score: number
|
||||
}>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const searchUsersTool: ToolConfig<SearchUsersParams, SearchUsersResponse> = {
|
||||
id: 'github_search_users',
|
||||
name: 'GitHub Search Users',
|
||||
description:
|
||||
'Search for users and organizations on GitHub. Use qualifiers like type:user, type:org, followers:>1000, repos:>10, location:city',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
q: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Search query with optional qualifiers (type:user/org, followers:, repos:, location:, language:, created:)',
|
||||
},
|
||||
sort: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort by: followers, repositories, joined (default: best match)',
|
||||
},
|
||||
order: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort order: asc or desc (default: desc)',
|
||||
},
|
||||
per_page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Results per page (max 100, default: 30)',
|
||||
default: 30,
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number (default: 1)',
|
||||
default: 1,
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://api.github.com/search/users')
|
||||
url.searchParams.append('q', params.q)
|
||||
if (params.sort) url.searchParams.append('sort', params.sort)
|
||||
if (params.order) url.searchParams.append('order', params.order)
|
||||
if (params.per_page) url.searchParams.append('per_page', String(params.per_page))
|
||||
if (params.page) url.searchParams.append('page', String(params.page))
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const items = data.items.map((item: any) => ({
|
||||
id: item.id,
|
||||
login: item.login,
|
||||
html_url: item.html_url,
|
||||
avatar_url: item.avatar_url,
|
||||
type: item.type,
|
||||
score: item.score,
|
||||
}))
|
||||
|
||||
const content = `Found ${data.total_count} user(s)/organization(s)${data.incomplete_results ? ' (incomplete)' : ''}:
|
||||
${items.map((item: any) => `@${item.login} (${item.type}) - ${item.html_url}`).join('\n')}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
total_count: data.total_count,
|
||||
incomplete_results: data.incomplete_results,
|
||||
items,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable search results' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Search results metadata',
|
||||
properties: {
|
||||
total_count: { type: 'number', description: 'Total matching results' },
|
||||
incomplete_results: { type: 'boolean', description: 'Whether results are incomplete' },
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Array of users/orgs',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'User ID' },
|
||||
login: { type: 'string', description: 'Username' },
|
||||
html_url: { type: 'string', description: 'Profile URL' },
|
||||
avatar_url: { type: 'string', description: 'Avatar URL' },
|
||||
type: { type: 'string', description: 'User or Organization' },
|
||||
score: { type: 'number', description: 'Search relevance score' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const searchUsersV2Tool: ToolConfig<SearchUsersParams, any> = {
|
||||
id: 'github_search_users_v2',
|
||||
name: searchUsersTool.name,
|
||||
description: searchUsersTool.description,
|
||||
version: '2.0.0',
|
||||
params: searchUsersTool.params,
|
||||
request: searchUsersTool.request,
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
total_count: data.total_count,
|
||||
incomplete_results: data.incomplete_results,
|
||||
items: data.items,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
total_count: { type: 'number', description: 'Total matching results' },
|
||||
incomplete_results: { type: 'boolean', description: 'Whether results are incomplete' },
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Array of user objects from GitHub API',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'User ID' },
|
||||
login: { type: 'string', description: 'Username' },
|
||||
html_url: { type: 'string', description: 'Profile URL' },
|
||||
avatar_url: { type: 'string', description: 'Avatar URL' },
|
||||
type: { type: 'string', description: 'User or Organization' },
|
||||
site_admin: { type: 'boolean', description: 'Is site admin' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
104
apps/sim/tools/github/star_gist.ts
Normal file
104
apps/sim/tools/github/star_gist.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface StarGistParams {
|
||||
gist_id: string
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface StarGistResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
starred: boolean
|
||||
gist_id: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const starGistTool: ToolConfig<StarGistParams, StarGistResponse> = {
|
||||
id: 'github_star_gist',
|
||||
name: 'GitHub Star Gist',
|
||||
description: 'Star a gist',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
gist_id: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The gist ID to star',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.github.com/gists/${params.gist_id?.trim()}/star`,
|
||||
method: 'PUT',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Length': '0',
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response, params) => {
|
||||
const starred = response.status === 204
|
||||
|
||||
return {
|
||||
success: starred,
|
||||
output: {
|
||||
content: starred
|
||||
? `Successfully starred gist ${params?.gist_id}`
|
||||
: `Failed to star gist ${params?.gist_id}`,
|
||||
metadata: {
|
||||
starred,
|
||||
gist_id: params?.gist_id ?? '',
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable result' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Star operation metadata',
|
||||
properties: {
|
||||
starred: { type: 'boolean', description: 'Whether starring succeeded' },
|
||||
gist_id: { type: 'string', description: 'The gist ID' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const starGistV2Tool: ToolConfig<StarGistParams, any> = {
|
||||
id: 'github_star_gist_v2',
|
||||
name: starGistTool.name,
|
||||
description: starGistTool.description,
|
||||
version: '2.0.0',
|
||||
params: starGistTool.params,
|
||||
request: starGistTool.request,
|
||||
|
||||
transformResponse: async (response: Response, params) => {
|
||||
const starred = response.status === 204
|
||||
return {
|
||||
success: starred,
|
||||
output: {
|
||||
starred,
|
||||
gist_id: params?.gist_id ?? '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
starred: { type: 'boolean', description: 'Whether starring succeeded' },
|
||||
gist_id: { type: 'string', description: 'The gist ID' },
|
||||
},
|
||||
}
|
||||
116
apps/sim/tools/github/star_repo.ts
Normal file
116
apps/sim/tools/github/star_repo.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface StarRepoParams {
|
||||
owner: string
|
||||
repo: string
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface StarRepoResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
starred: boolean
|
||||
owner: string
|
||||
repo: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const starRepoTool: ToolConfig<StarRepoParams, StarRepoResponse> = {
|
||||
id: 'github_star_repo',
|
||||
name: 'GitHub Star Repository',
|
||||
description: 'Star a repository',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.github.com/user/starred/${params.owner}/${params.repo}`,
|
||||
method: 'PUT',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Length': '0',
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response, params) => {
|
||||
const starred = response.status === 204
|
||||
|
||||
return {
|
||||
success: starred,
|
||||
output: {
|
||||
content: starred
|
||||
? `Successfully starred ${params?.owner}/${params?.repo}`
|
||||
: `Failed to star ${params?.owner}/${params?.repo}`,
|
||||
metadata: {
|
||||
starred,
|
||||
owner: params?.owner ?? '',
|
||||
repo: params?.repo ?? '',
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable result' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Star operation metadata',
|
||||
properties: {
|
||||
starred: { type: 'boolean', description: 'Whether starring succeeded' },
|
||||
owner: { type: 'string', description: 'Repository owner' },
|
||||
repo: { type: 'string', description: 'Repository name' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const starRepoV2Tool: ToolConfig<StarRepoParams, any> = {
|
||||
id: 'github_star_repo_v2',
|
||||
name: starRepoTool.name,
|
||||
description: starRepoTool.description,
|
||||
version: '2.0.0',
|
||||
params: starRepoTool.params,
|
||||
request: starRepoTool.request,
|
||||
|
||||
transformResponse: async (response: Response, params) => {
|
||||
const starred = response.status === 204
|
||||
return {
|
||||
success: starred,
|
||||
output: {
|
||||
starred,
|
||||
owner: params?.owner ?? '',
|
||||
repo: params?.repo ?? '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
starred: { type: 'boolean', description: 'Whether starring succeeded' },
|
||||
owner: { type: 'string', description: 'Repository owner' },
|
||||
repo: { type: 'string', description: 'Repository name' },
|
||||
},
|
||||
}
|
||||
103
apps/sim/tools/github/unstar_gist.ts
Normal file
103
apps/sim/tools/github/unstar_gist.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface UnstarGistParams {
|
||||
gist_id: string
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface UnstarGistResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
unstarred: boolean
|
||||
gist_id: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const unstarGistTool: ToolConfig<UnstarGistParams, UnstarGistResponse> = {
|
||||
id: 'github_unstar_gist',
|
||||
name: 'GitHub Unstar Gist',
|
||||
description: 'Unstar a gist',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
gist_id: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The gist ID to unstar',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.github.com/gists/${params.gist_id?.trim()}/star`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response, params) => {
|
||||
const unstarred = response.status === 204
|
||||
|
||||
return {
|
||||
success: unstarred,
|
||||
output: {
|
||||
content: unstarred
|
||||
? `Successfully unstarred gist ${params?.gist_id}`
|
||||
: `Failed to unstar gist ${params?.gist_id}`,
|
||||
metadata: {
|
||||
unstarred,
|
||||
gist_id: params?.gist_id ?? '',
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable result' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Unstar operation metadata',
|
||||
properties: {
|
||||
unstarred: { type: 'boolean', description: 'Whether unstarring succeeded' },
|
||||
gist_id: { type: 'string', description: 'The gist ID' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const unstarGistV2Tool: ToolConfig<UnstarGistParams, any> = {
|
||||
id: 'github_unstar_gist_v2',
|
||||
name: unstarGistTool.name,
|
||||
description: unstarGistTool.description,
|
||||
version: '2.0.0',
|
||||
params: unstarGistTool.params,
|
||||
request: unstarGistTool.request,
|
||||
|
||||
transformResponse: async (response: Response, params) => {
|
||||
const unstarred = response.status === 204
|
||||
return {
|
||||
success: unstarred,
|
||||
output: {
|
||||
unstarred,
|
||||
gist_id: params?.gist_id ?? '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
unstarred: { type: 'boolean', description: 'Whether unstarring succeeded' },
|
||||
gist_id: { type: 'string', description: 'The gist ID' },
|
||||
},
|
||||
}
|
||||
115
apps/sim/tools/github/unstar_repo.ts
Normal file
115
apps/sim/tools/github/unstar_repo.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface UnstarRepoParams {
|
||||
owner: string
|
||||
repo: string
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface UnstarRepoResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
unstarred: boolean
|
||||
owner: string
|
||||
repo: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const unstarRepoTool: ToolConfig<UnstarRepoParams, UnstarRepoResponse> = {
|
||||
id: 'github_unstar_repo',
|
||||
name: 'GitHub Unstar Repository',
|
||||
description: 'Remove star from a repository',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.github.com/user/starred/${params.owner}/${params.repo}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response, params) => {
|
||||
const unstarred = response.status === 204
|
||||
|
||||
return {
|
||||
success: unstarred,
|
||||
output: {
|
||||
content: unstarred
|
||||
? `Successfully unstarred ${params?.owner}/${params?.repo}`
|
||||
: `Failed to unstar ${params?.owner}/${params?.repo}`,
|
||||
metadata: {
|
||||
unstarred,
|
||||
owner: params?.owner ?? '',
|
||||
repo: params?.repo ?? '',
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable result' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Unstar operation metadata',
|
||||
properties: {
|
||||
unstarred: { type: 'boolean', description: 'Whether unstarring succeeded' },
|
||||
owner: { type: 'string', description: 'Repository owner' },
|
||||
repo: { type: 'string', description: 'Repository name' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const unstarRepoV2Tool: ToolConfig<UnstarRepoParams, any> = {
|
||||
id: 'github_unstar_repo_v2',
|
||||
name: unstarRepoTool.name,
|
||||
description: unstarRepoTool.description,
|
||||
version: '2.0.0',
|
||||
params: unstarRepoTool.params,
|
||||
request: unstarRepoTool.request,
|
||||
|
||||
transformResponse: async (response: Response, params) => {
|
||||
const unstarred = response.status === 204
|
||||
return {
|
||||
success: unstarred,
|
||||
output: {
|
||||
unstarred,
|
||||
owner: params?.owner ?? '',
|
||||
repo: params?.repo ?? '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
unstarred: { type: 'boolean', description: 'Whether unstarring succeeded' },
|
||||
owner: { type: 'string', description: 'Repository owner' },
|
||||
repo: { type: 'string', description: 'Repository name' },
|
||||
},
|
||||
}
|
||||
164
apps/sim/tools/github/update_gist.ts
Normal file
164
apps/sim/tools/github/update_gist.ts
Normal file
@@ -0,0 +1,164 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface UpdateGistParams {
|
||||
gist_id: string
|
||||
description?: string
|
||||
files?: string
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface UpdateGistResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
id: string
|
||||
html_url: string
|
||||
description: string | null
|
||||
public: boolean
|
||||
updated_at: string
|
||||
files: Record<
|
||||
string,
|
||||
{ filename: string; type: string; language: string | null; size: number }
|
||||
>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const updateGistTool: ToolConfig<UpdateGistParams, UpdateGistResponse> = {
|
||||
id: 'github_update_gist',
|
||||
name: 'GitHub Update Gist',
|
||||
description:
|
||||
'Update a gist description or files. To delete a file, set its value to null in files object',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
gist_id: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The gist ID to update',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New description for the gist',
|
||||
},
|
||||
files: {
|
||||
type: 'json',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'JSON object with filenames as keys. Set to null to delete, or provide content to update/add',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.github.com/gists/${params.gist_id?.trim()}`,
|
||||
method: 'PATCH',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, any> = {}
|
||||
if (params.description !== undefined) body.description = params.description
|
||||
if (params.files) {
|
||||
body.files = typeof params.files === 'string' ? JSON.parse(params.files) : params.files
|
||||
}
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const files: Record<
|
||||
string,
|
||||
{ filename: string; type: string; language: string | null; size: number }
|
||||
> = {}
|
||||
for (const [key, value] of Object.entries(data.files ?? {})) {
|
||||
const file = value as any
|
||||
files[key] = {
|
||||
filename: file.filename,
|
||||
type: file.type,
|
||||
language: file.language ?? null,
|
||||
size: file.size,
|
||||
}
|
||||
}
|
||||
|
||||
const content = `Updated gist: ${data.html_url}
|
||||
Description: ${data.description ?? 'No description'}
|
||||
Files: ${Object.keys(files).join(', ')}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
id: data.id,
|
||||
html_url: data.html_url,
|
||||
description: data.description ?? null,
|
||||
public: data.public,
|
||||
updated_at: data.updated_at,
|
||||
files,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable result' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Updated gist metadata',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Gist ID' },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
description: { type: 'string', description: 'Description', optional: true },
|
||||
public: { type: 'boolean', description: 'Is public' },
|
||||
updated_at: { type: 'string', description: 'Update date' },
|
||||
files: { type: 'object', description: 'Current files' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const updateGistV2Tool: ToolConfig<UpdateGistParams, any> = {
|
||||
id: 'github_update_gist_v2',
|
||||
name: updateGistTool.name,
|
||||
description: updateGistTool.description,
|
||||
version: '2.0.0',
|
||||
params: updateGistTool.params,
|
||||
request: updateGistTool.request,
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
...data,
|
||||
description: data.description ?? null,
|
||||
files: data.files ?? {},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Gist ID' },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
description: { type: 'string', description: 'Description', optional: true },
|
||||
public: { type: 'boolean', description: 'Is public' },
|
||||
updated_at: { type: 'string', description: 'Update date' },
|
||||
files: { type: 'object', description: 'Current files' },
|
||||
},
|
||||
}
|
||||
186
apps/sim/tools/github/update_milestone.ts
Normal file
186
apps/sim/tools/github/update_milestone.ts
Normal file
@@ -0,0 +1,186 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface UpdateMilestoneParams {
|
||||
owner: string
|
||||
repo: string
|
||||
milestone_number: number
|
||||
title?: string
|
||||
state?: 'open' | 'closed'
|
||||
description?: string
|
||||
due_on?: string
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
interface UpdateMilestoneResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
number: number
|
||||
title: string
|
||||
description: string | null
|
||||
state: string
|
||||
html_url: string
|
||||
due_on: string | null
|
||||
open_issues: number
|
||||
closed_issues: number
|
||||
updated_at: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const updateMilestoneTool: ToolConfig<UpdateMilestoneParams, UpdateMilestoneResponse> = {
|
||||
id: 'github_update_milestone',
|
||||
name: 'GitHub Update Milestone',
|
||||
description: 'Update a milestone in a repository',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
owner: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository owner',
|
||||
},
|
||||
repo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Repository name',
|
||||
},
|
||||
milestone_number: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Milestone number to update',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New milestone title',
|
||||
},
|
||||
state: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New state: open or closed',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New description',
|
||||
},
|
||||
due_on: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New due date (ISO 8601 format)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub API token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.github.com/repos/${params.owner}/${params.repo}/milestones/${params.milestone_number}`,
|
||||
method: 'PATCH',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, any> = {}
|
||||
if (params.title !== undefined) body.title = params.title
|
||||
if (params.state !== undefined) body.state = params.state
|
||||
if (params.description !== undefined) body.description = params.description
|
||||
if (params.due_on !== undefined) body.due_on = params.due_on
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const content = `Updated milestone: ${data.title} (#${data.number})
|
||||
State: ${data.state}
|
||||
Due: ${data.due_on ?? 'No due date'}
|
||||
${data.html_url}`
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
number: data.number,
|
||||
title: data.title,
|
||||
description: data.description ?? null,
|
||||
state: data.state,
|
||||
html_url: data.html_url,
|
||||
due_on: data.due_on ?? null,
|
||||
open_issues: data.open_issues,
|
||||
closed_issues: data.closed_issues,
|
||||
updated_at: data.updated_at,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Human-readable result' },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Updated milestone metadata',
|
||||
properties: {
|
||||
number: { type: 'number', description: 'Milestone number' },
|
||||
title: { type: 'string', description: 'Title' },
|
||||
description: { type: 'string', description: 'Description', optional: true },
|
||||
state: { type: 'string', description: 'State' },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
due_on: { type: 'string', description: 'Due date', optional: true },
|
||||
open_issues: { type: 'number', description: 'Open issues' },
|
||||
closed_issues: { type: 'number', description: 'Closed issues' },
|
||||
updated_at: { type: 'string', description: 'Update date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const updateMilestoneV2Tool: ToolConfig<UpdateMilestoneParams, any> = {
|
||||
id: 'github_update_milestone_v2',
|
||||
name: updateMilestoneTool.name,
|
||||
description: updateMilestoneTool.description,
|
||||
version: '2.0.0',
|
||||
params: updateMilestoneTool.params,
|
||||
request: updateMilestoneTool.request,
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
...data,
|
||||
description: data.description ?? null,
|
||||
due_on: data.due_on ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
number: { type: 'number', description: 'Milestone number' },
|
||||
title: { type: 'string', description: 'Title' },
|
||||
description: { type: 'string', description: 'Description', optional: true },
|
||||
state: { type: 'string', description: 'State' },
|
||||
html_url: { type: 'string', description: 'Web URL' },
|
||||
due_on: { type: 'string', description: 'Due date', optional: true },
|
||||
open_issues: { type: 'number', description: 'Open issues' },
|
||||
closed_issues: { type: 'number', description: 'Closed issues' },
|
||||
},
|
||||
}
|
||||
@@ -14,16 +14,19 @@ export const gitlabCancelPipelineTool: ToolConfig<
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitLab Personal Access Token',
|
||||
},
|
||||
projectId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Project ID or URL-encoded path',
|
||||
},
|
||||
pipelineId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pipeline ID',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -12,46 +12,55 @@ export const gitlabCreateIssueTool: ToolConfig<GitLabCreateIssueParams, GitLabCr
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitLab Personal Access Token',
|
||||
},
|
||||
projectId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Project ID or URL-encoded path',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Issue title',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Issue description (Markdown supported)',
|
||||
},
|
||||
labels: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of label names',
|
||||
},
|
||||
assigneeIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Array of user IDs to assign',
|
||||
},
|
||||
milestoneId: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Milestone ID to assign',
|
||||
},
|
||||
dueDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Due date in YYYY-MM-DD format',
|
||||
},
|
||||
confidential: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether the issue is confidential',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -14,21 +14,25 @@ export const gitlabCreateIssueNoteTool: ToolConfig<
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitLab Personal Access Token',
|
||||
},
|
||||
projectId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Project ID or URL-encoded path',
|
||||
},
|
||||
issueIid: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Issue internal ID (IID)',
|
||||
},
|
||||
body: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comment body (Markdown supported)',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -17,61 +17,73 @@ export const gitlabCreateMergeRequestTool: ToolConfig<
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitLab Personal Access Token',
|
||||
},
|
||||
projectId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Project ID or URL-encoded path',
|
||||
},
|
||||
sourceBranch: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Source branch name',
|
||||
},
|
||||
targetBranch: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Target branch name',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Merge request title',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Merge request description (Markdown supported)',
|
||||
},
|
||||
labels: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of label names',
|
||||
},
|
||||
assigneeIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Array of user IDs to assign',
|
||||
},
|
||||
milestoneId: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Milestone ID to assign',
|
||||
},
|
||||
removeSourceBranch: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Delete source branch after merge',
|
||||
},
|
||||
squash: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Squash commits on merge',
|
||||
},
|
||||
draft: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Mark as draft (work in progress)',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -17,21 +17,25 @@ export const gitlabCreateMergeRequestNoteTool: ToolConfig<
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitLab Personal Access Token',
|
||||
},
|
||||
projectId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Project ID or URL-encoded path',
|
||||
},
|
||||
mergeRequestIid: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Merge request internal ID (IID)',
|
||||
},
|
||||
body: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comment body (Markdown supported)',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -14,21 +14,25 @@ export const gitlabCreatePipelineTool: ToolConfig<
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitLab Personal Access Token',
|
||||
},
|
||||
projectId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Project ID or URL-encoded path',
|
||||
},
|
||||
ref: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Branch or tag to run the pipeline on',
|
||||
},
|
||||
variables: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Array of variables for the pipeline (each with key, value, and optional variable_type)',
|
||||
},
|
||||
|
||||
@@ -12,16 +12,19 @@ export const gitlabDeleteIssueTool: ToolConfig<GitLabDeleteIssueParams, GitLabDe
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitLab Personal Access Token',
|
||||
},
|
||||
projectId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Project ID or URL-encoded path',
|
||||
},
|
||||
issueIid: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Issue internal ID (IID)',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -11,16 +11,19 @@ export const gitlabGetIssueTool: ToolConfig<GitLabGetIssueParams, GitLabGetIssue
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitLab Personal Access Token',
|
||||
},
|
||||
projectId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Project ID or URL-encoded path',
|
||||
},
|
||||
issueIid: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Issue number within the project (the # shown in GitLab UI)',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -17,16 +17,19 @@ export const gitlabGetMergeRequestTool: ToolConfig<
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitLab Personal Access Token',
|
||||
},
|
||||
projectId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Project ID or URL-encoded path',
|
||||
},
|
||||
mergeRequestIid: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Merge request internal ID (IID)',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -12,16 +12,19 @@ export const gitlabGetPipelineTool: ToolConfig<GitLabGetPipelineParams, GitLabGe
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitLab Personal Access Token',
|
||||
},
|
||||
projectId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Project ID or URL-encoded path',
|
||||
},
|
||||
pipelineId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pipeline ID',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -11,11 +11,13 @@ export const gitlabGetProjectTool: ToolConfig<GitLabGetProjectParams, GitLabGetP
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitLab Personal Access Token',
|
||||
},
|
||||
projectId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Project ID or URL-encoded path (e.g., "namespace/project")',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -11,56 +11,67 @@ export const gitlabListIssuesTool: ToolConfig<GitLabListIssuesParams, GitLabList
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitLab Personal Access Token',
|
||||
},
|
||||
projectId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Project ID or URL-encoded path',
|
||||
},
|
||||
state: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by state (opened, closed, all)',
|
||||
},
|
||||
labels: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of label names',
|
||||
},
|
||||
assigneeId: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by assignee user ID',
|
||||
},
|
||||
milestoneTitle: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by milestone title',
|
||||
},
|
||||
search: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Search issues by title and description',
|
||||
},
|
||||
orderBy: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Order by field (created_at, updated_at)',
|
||||
},
|
||||
sort: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort direction (asc, desc)',
|
||||
},
|
||||
perPage: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of results per page (default 20, max 100)',
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number for pagination',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -17,51 +17,61 @@ export const gitlabListMergeRequestsTool: ToolConfig<
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitLab Personal Access Token',
|
||||
},
|
||||
projectId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Project ID or URL-encoded path',
|
||||
},
|
||||
state: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by state (opened, closed, merged, all)',
|
||||
},
|
||||
labels: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of label names',
|
||||
},
|
||||
sourceBranch: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by source branch',
|
||||
},
|
||||
targetBranch: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by target branch',
|
||||
},
|
||||
orderBy: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Order by field (created_at, updated_at)',
|
||||
},
|
||||
sort: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort direction (asc, desc)',
|
||||
},
|
||||
perPage: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of results per page (default 20, max 100)',
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number for pagination',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -14,42 +14,50 @@ export const gitlabListPipelinesTool: ToolConfig<
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitLab Personal Access Token',
|
||||
},
|
||||
projectId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Project ID or URL-encoded path',
|
||||
},
|
||||
ref: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by ref (branch or tag)',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Filter by status (created, waiting_for_resource, preparing, pending, running, success, failed, canceled, skipped, manual, scheduled)',
|
||||
},
|
||||
orderBy: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Order by field (id, status, ref, updated_at, user_id)',
|
||||
},
|
||||
sort: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort direction (asc, desc)',
|
||||
},
|
||||
perPage: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of results per page (default 20, max 100)',
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number for pagination',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -14,46 +14,55 @@ export const gitlabListProjectsTool: ToolConfig<
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitLab Personal Access Token',
|
||||
},
|
||||
owned: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Limit to projects owned by the current user',
|
||||
},
|
||||
membership: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Limit to projects the current user is a member of',
|
||||
},
|
||||
search: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Search projects by name',
|
||||
},
|
||||
visibility: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by visibility (public, internal, private)',
|
||||
},
|
||||
orderBy: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Order by field (id, name, path, created_at, updated_at, last_activity_at)',
|
||||
},
|
||||
sort: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort direction (asc, desc)',
|
||||
},
|
||||
perPage: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of results per page (default 20, max 100)',
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number for pagination',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -17,41 +17,49 @@ export const gitlabMergeMergeRequestTool: ToolConfig<
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitLab Personal Access Token',
|
||||
},
|
||||
projectId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Project ID or URL-encoded path',
|
||||
},
|
||||
mergeRequestIid: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Merge request internal ID (IID)',
|
||||
},
|
||||
mergeCommitMessage: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Custom merge commit message',
|
||||
},
|
||||
squashCommitMessage: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Custom squash commit message',
|
||||
},
|
||||
squash: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Squash commits before merging',
|
||||
},
|
||||
shouldRemoveSourceBranch: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Delete source branch after merge',
|
||||
},
|
||||
mergeWhenPipelineSucceeds: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Merge when pipeline succeeds',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -14,16 +14,19 @@ export const gitlabRetryPipelineTool: ToolConfig<
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitLab Personal Access Token',
|
||||
},
|
||||
projectId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Project ID or URL-encoded path',
|
||||
},
|
||||
pipelineId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pipeline ID',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -12,56 +12,67 @@ export const gitlabUpdateIssueTool: ToolConfig<GitLabUpdateIssueParams, GitLabUp
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitLab Personal Access Token',
|
||||
},
|
||||
projectId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Project ID or URL-encoded path',
|
||||
},
|
||||
issueIid: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Issue internal ID (IID)',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New issue title',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New issue description (Markdown supported)',
|
||||
},
|
||||
stateEvent: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'State event (close or reopen)',
|
||||
},
|
||||
labels: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of label names',
|
||||
},
|
||||
assigneeIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Array of user IDs to assign',
|
||||
},
|
||||
milestoneId: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Milestone ID to assign',
|
||||
},
|
||||
dueDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Due date in YYYY-MM-DD format',
|
||||
},
|
||||
confidential: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether the issue is confidential',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -17,66 +17,79 @@ export const gitlabUpdateMergeRequestTool: ToolConfig<
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitLab Personal Access Token',
|
||||
},
|
||||
projectId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Project ID or URL-encoded path',
|
||||
},
|
||||
mergeRequestIid: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Merge request internal ID (IID)',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New merge request title',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New merge request description',
|
||||
},
|
||||
stateEvent: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'State event (close or reopen)',
|
||||
},
|
||||
labels: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of label names',
|
||||
},
|
||||
assigneeIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Array of user IDs to assign',
|
||||
},
|
||||
milestoneId: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Milestone ID to assign',
|
||||
},
|
||||
targetBranch: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New target branch',
|
||||
},
|
||||
removeSourceBranch: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Delete source branch after merge',
|
||||
},
|
||||
squash: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Squash commits on merge',
|
||||
},
|
||||
draft: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Mark as draft (work in progress)',
|
||||
},
|
||||
},
|
||||
|
||||
135
apps/sim/tools/google_calendar/delete.ts
Normal file
135
apps/sim/tools/google_calendar/delete.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import { CALENDAR_API_BASE, type GoogleCalendarDeleteParams } from '@/tools/google_calendar/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface GoogleCalendarDeleteResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
eventId: string
|
||||
deleted: boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const deleteTool: ToolConfig<GoogleCalendarDeleteParams, GoogleCalendarDeleteResponse> = {
|
||||
id: 'google_calendar_delete',
|
||||
name: 'Google Calendar Delete Event',
|
||||
description: 'Delete an event from Google Calendar',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-calendar',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'Access token for Google Calendar API',
|
||||
},
|
||||
calendarId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Calendar ID (defaults to primary)',
|
||||
},
|
||||
eventId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Event ID to delete',
|
||||
},
|
||||
sendUpdates: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'How to send updates to attendees: all, externalOnly, or none',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: GoogleCalendarDeleteParams) => {
|
||||
const calendarId = params.calendarId || 'primary'
|
||||
const queryParams = new URLSearchParams()
|
||||
|
||||
if (params.sendUpdates !== undefined) {
|
||||
queryParams.append('sendUpdates', params.sendUpdates)
|
||||
}
|
||||
|
||||
const queryString = queryParams.toString()
|
||||
return `${CALENDAR_API_BASE}/calendars/${encodeURIComponent(calendarId)}/events/${encodeURIComponent(params.eventId)}${queryString ? `?${queryString}` : ''}`
|
||||
},
|
||||
method: 'DELETE',
|
||||
headers: (params: GoogleCalendarDeleteParams) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params) => {
|
||||
// DELETE returns 204 No Content on success
|
||||
if (response.status === 204 || response.ok) {
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content: `Event successfully deleted`,
|
||||
metadata: {
|
||||
eventId: params?.eventId || '',
|
||||
deleted: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const errorData = await response.json()
|
||||
throw new Error(errorData.error?.message || 'Failed to delete event')
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Event deletion confirmation message' },
|
||||
metadata: {
|
||||
type: 'json',
|
||||
description: 'Deletion details including event ID',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
interface GoogleCalendarDeleteV2Response {
|
||||
success: boolean
|
||||
output: {
|
||||
eventId: string
|
||||
deleted: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export const deleteV2Tool: ToolConfig<GoogleCalendarDeleteParams, GoogleCalendarDeleteV2Response> =
|
||||
{
|
||||
id: 'google_calendar_delete_v2',
|
||||
name: 'Google Calendar Delete Event',
|
||||
description: 'Delete an event from Google Calendar. Returns API-aligned fields only.',
|
||||
version: '2.0.0',
|
||||
oauth: deleteTool.oauth,
|
||||
params: deleteTool.params,
|
||||
request: deleteTool.request,
|
||||
transformResponse: async (response: Response, params) => {
|
||||
if (response.status === 204 || response.ok) {
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
eventId: params?.eventId || '',
|
||||
deleted: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const errorData = await response.json()
|
||||
throw new Error(errorData.error?.message || 'Failed to delete event')
|
||||
},
|
||||
outputs: {
|
||||
eventId: { type: 'string', description: 'Deleted event ID' },
|
||||
deleted: { type: 'boolean', description: 'Whether deletion was successful' },
|
||||
},
|
||||
}
|
||||
@@ -1,17 +1,32 @@
|
||||
import { createTool, createV2Tool } from '@/tools/google_calendar/create'
|
||||
import { deleteTool, deleteV2Tool } from '@/tools/google_calendar/delete'
|
||||
import { getTool, getV2Tool } from '@/tools/google_calendar/get'
|
||||
import { instancesTool, instancesV2Tool } from '@/tools/google_calendar/instances'
|
||||
import { inviteTool, inviteV2Tool } from '@/tools/google_calendar/invite'
|
||||
import { listTool, listV2Tool } from '@/tools/google_calendar/list'
|
||||
import { listCalendarsTool, listCalendarsV2Tool } from '@/tools/google_calendar/list_calendars'
|
||||
import { moveTool, moveV2Tool } from '@/tools/google_calendar/move'
|
||||
import { quickAddTool, quickAddV2Tool } from '@/tools/google_calendar/quick_add'
|
||||
import { updateTool, updateV2Tool } from '@/tools/google_calendar/update'
|
||||
|
||||
export const googleCalendarCreateTool = createTool
|
||||
export const googleCalendarDeleteTool = deleteTool
|
||||
export const googleCalendarGetTool = getTool
|
||||
export const googleCalendarInstancesTool = instancesTool
|
||||
export const googleCalendarInviteTool = inviteTool
|
||||
export const googleCalendarListTool = listTool
|
||||
export const googleCalendarListCalendarsTool = listCalendarsTool
|
||||
export const googleCalendarMoveTool = moveTool
|
||||
export const googleCalendarQuickAddTool = quickAddTool
|
||||
export const googleCalendarUpdateTool = updateTool
|
||||
|
||||
export const googleCalendarCreateV2Tool = createV2Tool
|
||||
export const googleCalendarDeleteV2Tool = deleteV2Tool
|
||||
export const googleCalendarGetV2Tool = getV2Tool
|
||||
export const googleCalendarInstancesV2Tool = instancesV2Tool
|
||||
export const googleCalendarInviteV2Tool = inviteV2Tool
|
||||
export const googleCalendarListV2Tool = listV2Tool
|
||||
export const googleCalendarListCalendarsV2Tool = listCalendarsV2Tool
|
||||
export const googleCalendarMoveV2Tool = moveV2Tool
|
||||
export const googleCalendarQuickAddV2Tool = quickAddV2Tool
|
||||
export const googleCalendarUpdateV2Tool = updateV2Tool
|
||||
|
||||
268
apps/sim/tools/google_calendar/instances.ts
Normal file
268
apps/sim/tools/google_calendar/instances.ts
Normal file
@@ -0,0 +1,268 @@
|
||||
import {
|
||||
CALENDAR_API_BASE,
|
||||
type GoogleCalendarApiEventResponse,
|
||||
} from '@/tools/google_calendar/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface GoogleCalendarInstancesParams {
|
||||
accessToken: string
|
||||
calendarId?: string
|
||||
eventId: string
|
||||
timeMin?: string
|
||||
timeMax?: string
|
||||
maxResults?: number
|
||||
pageToken?: string
|
||||
showDeleted?: boolean
|
||||
}
|
||||
|
||||
interface GoogleCalendarInstancesResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
nextPageToken?: string
|
||||
timeZone: string
|
||||
instances: Array<{
|
||||
id: string
|
||||
htmlLink: string
|
||||
status: string
|
||||
summary: string
|
||||
description?: string
|
||||
location?: string
|
||||
start: {
|
||||
dateTime?: string
|
||||
date?: string
|
||||
timeZone?: string
|
||||
}
|
||||
end: {
|
||||
dateTime?: string
|
||||
date?: string
|
||||
timeZone?: string
|
||||
}
|
||||
attendees?: Array<{
|
||||
email: string
|
||||
displayName?: string
|
||||
responseStatus: string
|
||||
}>
|
||||
creator?: {
|
||||
email: string
|
||||
displayName?: string
|
||||
}
|
||||
organizer?: {
|
||||
email: string
|
||||
displayName?: string
|
||||
}
|
||||
recurringEventId: string
|
||||
originalStartTime: {
|
||||
dateTime?: string
|
||||
date?: string
|
||||
timeZone?: string
|
||||
}
|
||||
}>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface InstanceApiResponse {
|
||||
kind: string
|
||||
etag: string
|
||||
summary: string
|
||||
description?: string
|
||||
updated: string
|
||||
timeZone: string
|
||||
accessRole: string
|
||||
nextPageToken?: string
|
||||
items: Array<
|
||||
GoogleCalendarApiEventResponse & {
|
||||
recurringEventId: string
|
||||
originalStartTime: {
|
||||
dateTime?: string
|
||||
date?: string
|
||||
timeZone?: string
|
||||
}
|
||||
}
|
||||
>
|
||||
}
|
||||
|
||||
export const instancesTool: ToolConfig<
|
||||
GoogleCalendarInstancesParams,
|
||||
GoogleCalendarInstancesResponse
|
||||
> = {
|
||||
id: 'google_calendar_instances',
|
||||
name: 'Google Calendar Get Instances',
|
||||
description: 'Get instances of a recurring event from Google Calendar',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-calendar',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'Access token for Google Calendar API',
|
||||
},
|
||||
calendarId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Calendar ID (defaults to primary)',
|
||||
},
|
||||
eventId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Recurring event ID to get instances of',
|
||||
},
|
||||
timeMin: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lower bound for instances (RFC3339 timestamp, e.g., 2025-06-03T00:00:00Z)',
|
||||
},
|
||||
timeMax: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Upper bound for instances (RFC3339 timestamp, e.g., 2025-06-04T00:00:00Z)',
|
||||
},
|
||||
maxResults: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of instances to return (default 250, max 2500)',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Token for retrieving subsequent pages of results',
|
||||
},
|
||||
showDeleted: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'hidden',
|
||||
description: 'Include deleted instances',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: GoogleCalendarInstancesParams) => {
|
||||
const calendarId = params.calendarId || 'primary'
|
||||
const queryParams = new URLSearchParams()
|
||||
|
||||
if (params.timeMin) queryParams.append('timeMin', params.timeMin)
|
||||
if (params.timeMax) queryParams.append('timeMax', params.timeMax)
|
||||
if (params.maxResults) queryParams.append('maxResults', params.maxResults.toString())
|
||||
if (params.pageToken) queryParams.append('pageToken', params.pageToken)
|
||||
if (params.showDeleted !== undefined)
|
||||
queryParams.append('showDeleted', params.showDeleted.toString())
|
||||
|
||||
const queryString = queryParams.toString()
|
||||
return `${CALENDAR_API_BASE}/calendars/${encodeURIComponent(calendarId)}/events/${encodeURIComponent(params.eventId)}/instances${queryString ? `?${queryString}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params: GoogleCalendarInstancesParams) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data: InstanceApiResponse = await response.json()
|
||||
const instances = data.items || []
|
||||
const instancesCount = instances.length
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content: `Found ${instancesCount} instance${instancesCount !== 1 ? 's' : ''} of the recurring event`,
|
||||
metadata: {
|
||||
nextPageToken: data.nextPageToken,
|
||||
timeZone: data.timeZone,
|
||||
instances: instances.map((instance) => ({
|
||||
id: instance.id,
|
||||
htmlLink: instance.htmlLink,
|
||||
status: instance.status,
|
||||
summary: instance.summary || 'No title',
|
||||
description: instance.description,
|
||||
location: instance.location,
|
||||
start: instance.start,
|
||||
end: instance.end,
|
||||
attendees: instance.attendees,
|
||||
creator: instance.creator,
|
||||
organizer: instance.organizer,
|
||||
recurringEventId: instance.recurringEventId,
|
||||
originalStartTime: instance.originalStartTime,
|
||||
})),
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Summary of found instances count' },
|
||||
metadata: {
|
||||
type: 'json',
|
||||
description: 'List of recurring event instances with pagination tokens',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
interface GoogleCalendarInstancesV2Response {
|
||||
success: boolean
|
||||
output: {
|
||||
nextPageToken: string | null
|
||||
timeZone: string | null
|
||||
instances: Array<Record<string, any>>
|
||||
}
|
||||
}
|
||||
|
||||
export const instancesV2Tool: ToolConfig<
|
||||
GoogleCalendarInstancesParams,
|
||||
GoogleCalendarInstancesV2Response
|
||||
> = {
|
||||
id: 'google_calendar_instances_v2',
|
||||
name: 'Google Calendar Get Instances',
|
||||
description:
|
||||
'Get instances of a recurring event from Google Calendar. Returns API-aligned fields only.',
|
||||
version: '2.0.0',
|
||||
oauth: instancesTool.oauth,
|
||||
params: instancesTool.params,
|
||||
request: instancesTool.request,
|
||||
transformResponse: async (response: Response) => {
|
||||
const data: InstanceApiResponse = await response.json()
|
||||
const instances = data.items || []
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
nextPageToken: data.nextPageToken ?? null,
|
||||
timeZone: data.timeZone ?? null,
|
||||
instances: instances.map((instance) => ({
|
||||
id: instance.id,
|
||||
htmlLink: instance.htmlLink,
|
||||
status: instance.status,
|
||||
summary: instance.summary ?? null,
|
||||
description: instance.description ?? null,
|
||||
location: instance.location ?? null,
|
||||
start: instance.start,
|
||||
end: instance.end,
|
||||
attendees: instance.attendees ?? null,
|
||||
creator: instance.creator,
|
||||
organizer: instance.organizer,
|
||||
recurringEventId: instance.recurringEventId,
|
||||
originalStartTime: instance.originalStartTime,
|
||||
})),
|
||||
},
|
||||
}
|
||||
},
|
||||
outputs: {
|
||||
nextPageToken: { type: 'string', description: 'Next page token', optional: true },
|
||||
timeZone: { type: 'string', description: 'Calendar time zone', optional: true },
|
||||
instances: { type: 'json', description: 'List of recurring event instances' },
|
||||
},
|
||||
}
|
||||
280
apps/sim/tools/google_calendar/list_calendars.ts
Normal file
280
apps/sim/tools/google_calendar/list_calendars.ts
Normal file
@@ -0,0 +1,280 @@
|
||||
import { CALENDAR_API_BASE } from '@/tools/google_calendar/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface GoogleCalendarListCalendarsParams {
|
||||
accessToken: string
|
||||
minAccessRole?: 'freeBusyReader' | 'reader' | 'writer' | 'owner'
|
||||
maxResults?: number
|
||||
pageToken?: string
|
||||
showDeleted?: boolean
|
||||
showHidden?: boolean
|
||||
}
|
||||
|
||||
interface CalendarListEntry {
|
||||
kind: string
|
||||
etag: string
|
||||
id: string
|
||||
summary: string
|
||||
description?: string
|
||||
location?: string
|
||||
timeZone: string
|
||||
summaryOverride?: string
|
||||
colorId: string
|
||||
backgroundColor: string
|
||||
foregroundColor: string
|
||||
hidden?: boolean
|
||||
selected?: boolean
|
||||
accessRole: string
|
||||
defaultReminders: Array<{
|
||||
method: string
|
||||
minutes: number
|
||||
}>
|
||||
notificationSettings?: {
|
||||
notifications: Array<{
|
||||
type: string
|
||||
method: string
|
||||
}>
|
||||
}
|
||||
primary?: boolean
|
||||
deleted?: boolean
|
||||
conferenceProperties?: {
|
||||
allowedConferenceSolutionTypes: string[]
|
||||
}
|
||||
}
|
||||
|
||||
interface CalendarListApiResponse {
|
||||
kind: string
|
||||
etag: string
|
||||
nextPageToken?: string
|
||||
nextSyncToken?: string
|
||||
items: CalendarListEntry[]
|
||||
}
|
||||
|
||||
interface GoogleCalendarListCalendarsResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
nextPageToken?: string
|
||||
calendars: Array<{
|
||||
id: string
|
||||
summary: string
|
||||
description?: string
|
||||
location?: string
|
||||
timeZone: string
|
||||
accessRole: string
|
||||
backgroundColor: string
|
||||
foregroundColor: string
|
||||
primary?: boolean
|
||||
hidden?: boolean
|
||||
selected?: boolean
|
||||
}>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const listCalendarsTool: ToolConfig<
|
||||
GoogleCalendarListCalendarsParams,
|
||||
GoogleCalendarListCalendarsResponse
|
||||
> = {
|
||||
id: 'google_calendar_list_calendars',
|
||||
name: 'Google Calendar List Calendars',
|
||||
description: "List all calendars in the user's calendar list",
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-calendar',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'Access token for Google Calendar API',
|
||||
},
|
||||
minAccessRole: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description:
|
||||
'Minimum access role for returned calendars: freeBusyReader, reader, writer, or owner',
|
||||
},
|
||||
maxResults: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of calendars to return (default 100, max 250)',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Token for retrieving subsequent pages of results',
|
||||
},
|
||||
showDeleted: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'hidden',
|
||||
description: 'Include deleted calendars',
|
||||
},
|
||||
showHidden: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'hidden',
|
||||
description: 'Include hidden calendars',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: GoogleCalendarListCalendarsParams) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
|
||||
if (params.minAccessRole) queryParams.append('minAccessRole', params.minAccessRole)
|
||||
if (params.maxResults) queryParams.append('maxResults', params.maxResults.toString())
|
||||
if (params.pageToken) queryParams.append('pageToken', params.pageToken)
|
||||
if (params.showDeleted !== undefined)
|
||||
queryParams.append('showDeleted', params.showDeleted.toString())
|
||||
if (params.showHidden !== undefined)
|
||||
queryParams.append('showHidden', params.showHidden.toString())
|
||||
|
||||
const queryString = queryParams.toString()
|
||||
return `${CALENDAR_API_BASE}/users/me/calendarList${queryString ? `?${queryString}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params: GoogleCalendarListCalendarsParams) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data: CalendarListApiResponse = await response.json()
|
||||
const calendars = data.items || []
|
||||
const calendarsCount = calendars.length
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content: `Found ${calendarsCount} calendar${calendarsCount !== 1 ? 's' : ''}`,
|
||||
metadata: {
|
||||
nextPageToken: data.nextPageToken,
|
||||
calendars: calendars.map((calendar) => ({
|
||||
id: calendar.id,
|
||||
summary: calendar.summaryOverride || calendar.summary,
|
||||
description: calendar.description,
|
||||
location: calendar.location,
|
||||
timeZone: calendar.timeZone,
|
||||
accessRole: calendar.accessRole,
|
||||
backgroundColor: calendar.backgroundColor,
|
||||
foregroundColor: calendar.foregroundColor,
|
||||
primary: calendar.primary,
|
||||
hidden: calendar.hidden,
|
||||
selected: calendar.selected,
|
||||
})),
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Summary of found calendars count' },
|
||||
metadata: {
|
||||
type: 'json',
|
||||
description: 'List of calendars with their details',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
interface GoogleCalendarListCalendarsV2Response {
|
||||
success: boolean
|
||||
output: {
|
||||
nextPageToken: string | null
|
||||
calendars: Array<{
|
||||
id: string
|
||||
summary: string
|
||||
description: string | null
|
||||
location: string | null
|
||||
timeZone: string
|
||||
accessRole: string
|
||||
backgroundColor: string
|
||||
foregroundColor: string
|
||||
primary: boolean | null
|
||||
hidden: boolean | null
|
||||
selected: boolean | null
|
||||
}>
|
||||
}
|
||||
}
|
||||
|
||||
export const listCalendarsV2Tool: ToolConfig<
|
||||
GoogleCalendarListCalendarsParams,
|
||||
GoogleCalendarListCalendarsV2Response
|
||||
> = {
|
||||
id: 'google_calendar_list_calendars_v2',
|
||||
name: 'Google Calendar List Calendars',
|
||||
description: "List all calendars in the user's calendar list. Returns API-aligned fields only.",
|
||||
version: '2.0.0',
|
||||
oauth: listCalendarsTool.oauth,
|
||||
params: listCalendarsTool.params,
|
||||
request: listCalendarsTool.request,
|
||||
transformResponse: async (response: Response) => {
|
||||
const data: CalendarListApiResponse = await response.json()
|
||||
const calendars = data.items || []
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
nextPageToken: data.nextPageToken ?? null,
|
||||
calendars: calendars.map((calendar) => ({
|
||||
id: calendar.id,
|
||||
summary: calendar.summaryOverride || calendar.summary,
|
||||
description: calendar.description ?? null,
|
||||
location: calendar.location ?? null,
|
||||
timeZone: calendar.timeZone,
|
||||
accessRole: calendar.accessRole,
|
||||
backgroundColor: calendar.backgroundColor,
|
||||
foregroundColor: calendar.foregroundColor,
|
||||
primary: calendar.primary ?? null,
|
||||
hidden: calendar.hidden ?? null,
|
||||
selected: calendar.selected ?? null,
|
||||
})),
|
||||
},
|
||||
}
|
||||
},
|
||||
outputs: {
|
||||
nextPageToken: { type: 'string', description: 'Next page token', optional: true },
|
||||
calendars: {
|
||||
type: 'array',
|
||||
description: 'List of calendars',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Calendar ID' },
|
||||
summary: { type: 'string', description: 'Calendar title' },
|
||||
description: { type: 'string', description: 'Calendar description', optional: true },
|
||||
location: { type: 'string', description: 'Calendar location', optional: true },
|
||||
timeZone: { type: 'string', description: 'Calendar time zone' },
|
||||
accessRole: { type: 'string', description: 'Access role for the calendar' },
|
||||
backgroundColor: { type: 'string', description: 'Calendar background color' },
|
||||
foregroundColor: { type: 'string', description: 'Calendar foreground color' },
|
||||
primary: {
|
||||
type: 'boolean',
|
||||
description: 'Whether this is the primary calendar',
|
||||
optional: true,
|
||||
},
|
||||
hidden: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the calendar is hidden',
|
||||
optional: true,
|
||||
},
|
||||
selected: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the calendar is selected',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
208
apps/sim/tools/google_calendar/move.ts
Normal file
208
apps/sim/tools/google_calendar/move.ts
Normal file
@@ -0,0 +1,208 @@
|
||||
import {
|
||||
CALENDAR_API_BASE,
|
||||
type GoogleCalendarApiEventResponse,
|
||||
} from '@/tools/google_calendar/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface GoogleCalendarMoveParams {
|
||||
accessToken: string
|
||||
calendarId?: string
|
||||
eventId: string
|
||||
destinationCalendarId: string
|
||||
sendUpdates?: 'all' | 'externalOnly' | 'none'
|
||||
}
|
||||
|
||||
interface GoogleCalendarMoveResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
id: string
|
||||
htmlLink: string
|
||||
status: string
|
||||
summary: string
|
||||
description?: string
|
||||
location?: string
|
||||
start: {
|
||||
dateTime?: string
|
||||
date?: string
|
||||
timeZone?: string
|
||||
}
|
||||
end: {
|
||||
dateTime?: string
|
||||
date?: string
|
||||
timeZone?: string
|
||||
}
|
||||
attendees?: Array<{
|
||||
email: string
|
||||
displayName?: string
|
||||
responseStatus: string
|
||||
}>
|
||||
creator?: {
|
||||
email: string
|
||||
displayName?: string
|
||||
}
|
||||
organizer?: {
|
||||
email: string
|
||||
displayName?: string
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const moveTool: ToolConfig<GoogleCalendarMoveParams, GoogleCalendarMoveResponse> = {
|
||||
id: 'google_calendar_move',
|
||||
name: 'Google Calendar Move Event',
|
||||
description: 'Move an event to a different calendar',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-calendar',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'Access token for Google Calendar API',
|
||||
},
|
||||
calendarId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Source calendar ID (defaults to primary)',
|
||||
},
|
||||
eventId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Event ID to move',
|
||||
},
|
||||
destinationCalendarId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Destination calendar ID',
|
||||
},
|
||||
sendUpdates: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'How to send updates to attendees: all, externalOnly, or none',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: GoogleCalendarMoveParams) => {
|
||||
const calendarId = params.calendarId || 'primary'
|
||||
const queryParams = new URLSearchParams()
|
||||
|
||||
queryParams.append('destination', params.destinationCalendarId)
|
||||
|
||||
if (params.sendUpdates !== undefined) {
|
||||
queryParams.append('sendUpdates', params.sendUpdates)
|
||||
}
|
||||
|
||||
return `${CALENDAR_API_BASE}/calendars/${encodeURIComponent(calendarId)}/events/${encodeURIComponent(params.eventId)}/move?${queryParams.toString()}`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params: GoogleCalendarMoveParams) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data: GoogleCalendarApiEventResponse = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content: `Event "${data.summary}" moved successfully`,
|
||||
metadata: {
|
||||
id: data.id,
|
||||
htmlLink: data.htmlLink,
|
||||
status: data.status,
|
||||
summary: data.summary,
|
||||
description: data.description,
|
||||
location: data.location,
|
||||
start: data.start,
|
||||
end: data.end,
|
||||
attendees: data.attendees,
|
||||
creator: data.creator,
|
||||
organizer: data.organizer,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Event move confirmation message' },
|
||||
metadata: {
|
||||
type: 'json',
|
||||
description: 'Moved event metadata including new details',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
interface GoogleCalendarMoveV2Response {
|
||||
success: boolean
|
||||
output: {
|
||||
id: string
|
||||
htmlLink: string
|
||||
status: string
|
||||
summary: string | null
|
||||
description: string | null
|
||||
location: string | null
|
||||
start: any
|
||||
end: any
|
||||
attendees: any | null
|
||||
creator: any
|
||||
organizer: any
|
||||
}
|
||||
}
|
||||
|
||||
export const moveV2Tool: ToolConfig<GoogleCalendarMoveParams, GoogleCalendarMoveV2Response> = {
|
||||
id: 'google_calendar_move_v2',
|
||||
name: 'Google Calendar Move Event',
|
||||
description: 'Move an event to a different calendar. Returns API-aligned fields only.',
|
||||
version: '2.0.0',
|
||||
oauth: moveTool.oauth,
|
||||
params: moveTool.params,
|
||||
request: moveTool.request,
|
||||
transformResponse: async (response: Response) => {
|
||||
const data: GoogleCalendarApiEventResponse = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
htmlLink: data.htmlLink,
|
||||
status: data.status,
|
||||
summary: data.summary ?? null,
|
||||
description: data.description ?? null,
|
||||
location: data.location ?? null,
|
||||
start: data.start,
|
||||
end: data.end,
|
||||
attendees: data.attendees ?? null,
|
||||
creator: data.creator,
|
||||
organizer: data.organizer,
|
||||
},
|
||||
}
|
||||
},
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Event ID' },
|
||||
htmlLink: { type: 'string', description: 'Event link' },
|
||||
status: { type: 'string', description: 'Event status' },
|
||||
summary: { type: 'string', description: 'Event title', optional: true },
|
||||
description: { type: 'string', description: 'Event description', optional: true },
|
||||
location: { type: 'string', description: 'Event location', optional: true },
|
||||
start: { type: 'json', description: 'Event start' },
|
||||
end: { type: 'json', description: 'Event end' },
|
||||
attendees: { type: 'json', description: 'Event attendees', optional: true },
|
||||
creator: { type: 'json', description: 'Event creator' },
|
||||
organizer: { type: 'json', description: 'Event organizer' },
|
||||
},
|
||||
}
|
||||
@@ -75,6 +75,30 @@ export interface GoogleCalendarInviteParams extends BaseGoogleCalendarParams {
|
||||
replaceExisting?: boolean // Whether to replace existing attendees or add to them
|
||||
}
|
||||
|
||||
export interface GoogleCalendarMoveParams extends BaseGoogleCalendarParams {
|
||||
eventId: string
|
||||
destinationCalendarId: string
|
||||
sendUpdates?: 'all' | 'externalOnly' | 'none'
|
||||
}
|
||||
|
||||
export interface GoogleCalendarInstancesParams extends BaseGoogleCalendarParams {
|
||||
eventId: string
|
||||
timeMin?: string
|
||||
timeMax?: string
|
||||
maxResults?: number
|
||||
pageToken?: string
|
||||
showDeleted?: boolean
|
||||
}
|
||||
|
||||
export interface GoogleCalendarListCalendarsParams {
|
||||
accessToken: string
|
||||
minAccessRole?: 'freeBusyReader' | 'reader' | 'writer' | 'owner'
|
||||
maxResults?: number
|
||||
pageToken?: string
|
||||
showDeleted?: boolean
|
||||
showHidden?: boolean
|
||||
}
|
||||
|
||||
export type GoogleCalendarToolParams =
|
||||
| GoogleCalendarCreateParams
|
||||
| GoogleCalendarListParams
|
||||
@@ -83,6 +107,9 @@ export type GoogleCalendarToolParams =
|
||||
| GoogleCalendarDeleteParams
|
||||
| GoogleCalendarQuickAddParams
|
||||
| GoogleCalendarInviteParams
|
||||
| GoogleCalendarMoveParams
|
||||
| GoogleCalendarInstancesParams
|
||||
| GoogleCalendarListCalendarsParams
|
||||
|
||||
interface EventMetadata {
|
||||
id: string
|
||||
@@ -277,6 +304,65 @@ export interface GoogleCalendarApiListResponse {
|
||||
items: GoogleCalendarApiEventResponse[]
|
||||
}
|
||||
|
||||
export interface GoogleCalendarDeleteResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
eventId: string
|
||||
deleted: boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface GoogleCalendarMoveResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: EventMetadata
|
||||
}
|
||||
}
|
||||
|
||||
export interface GoogleCalendarInstancesResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
nextPageToken?: string
|
||||
timeZone: string
|
||||
instances: Array<
|
||||
EventMetadata & {
|
||||
recurringEventId: string
|
||||
originalStartTime: {
|
||||
dateTime?: string
|
||||
date?: string
|
||||
timeZone?: string
|
||||
}
|
||||
}
|
||||
>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface GoogleCalendarListCalendarsResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
nextPageToken?: string
|
||||
calendars: Array<{
|
||||
id: string
|
||||
summary: string
|
||||
description?: string
|
||||
location?: string
|
||||
timeZone: string
|
||||
accessRole: string
|
||||
backgroundColor: string
|
||||
foregroundColor: string
|
||||
primary?: boolean
|
||||
hidden?: boolean
|
||||
selected?: boolean
|
||||
}>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type GoogleCalendarResponse =
|
||||
| GoogleCalendarCreateResponse
|
||||
| GoogleCalendarListResponse
|
||||
@@ -284,3 +370,7 @@ export type GoogleCalendarResponse =
|
||||
| GoogleCalendarQuickAddResponse
|
||||
| GoogleCalendarInviteResponse
|
||||
| GoogleCalendarUpdateResponse
|
||||
| GoogleCalendarDeleteResponse
|
||||
| GoogleCalendarMoveResponse
|
||||
| GoogleCalendarInstancesResponse
|
||||
| GoogleCalendarListCalendarsResponse
|
||||
|
||||
255
apps/sim/tools/google_calendar/update.ts
Normal file
255
apps/sim/tools/google_calendar/update.ts
Normal file
@@ -0,0 +1,255 @@
|
||||
import {
|
||||
CALENDAR_API_BASE,
|
||||
type GoogleCalendarApiEventResponse,
|
||||
type GoogleCalendarUpdateParams,
|
||||
type GoogleCalendarUpdateResponse,
|
||||
} from '@/tools/google_calendar/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const updateTool: ToolConfig<GoogleCalendarUpdateParams, GoogleCalendarUpdateResponse> = {
|
||||
id: 'google_calendar_update',
|
||||
name: 'Google Calendar Update Event',
|
||||
description: 'Update an existing event in Google Calendar',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-calendar',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'Access token for Google Calendar API',
|
||||
},
|
||||
calendarId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Calendar ID (defaults to primary)',
|
||||
},
|
||||
eventId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Event ID to update',
|
||||
},
|
||||
summary: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New event title/summary',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New event description',
|
||||
},
|
||||
location: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New event location',
|
||||
},
|
||||
startDateTime: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'New start date and time. MUST include timezone offset (e.g., 2025-06-03T10:00:00-08:00) OR provide timeZone parameter',
|
||||
},
|
||||
endDateTime: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'New end date and time. MUST include timezone offset (e.g., 2025-06-03T11:00:00-08:00) OR provide timeZone parameter',
|
||||
},
|
||||
timeZone: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Time zone (e.g., America/Los_Angeles). Required if datetime does not include offset.',
|
||||
},
|
||||
attendees: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Array of attendee email addresses (replaces existing attendees)',
|
||||
},
|
||||
sendUpdates: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'How to send updates to attendees: all, externalOnly, or none',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: GoogleCalendarUpdateParams) => {
|
||||
const calendarId = params.calendarId || 'primary'
|
||||
const queryParams = new URLSearchParams()
|
||||
|
||||
if (params.sendUpdates !== undefined) {
|
||||
queryParams.append('sendUpdates', params.sendUpdates)
|
||||
}
|
||||
|
||||
const queryString = queryParams.toString()
|
||||
return `${CALENDAR_API_BASE}/calendars/${encodeURIComponent(calendarId)}/events/${encodeURIComponent(params.eventId)}${queryString ? `?${queryString}` : ''}`
|
||||
},
|
||||
method: 'PATCH',
|
||||
headers: (params: GoogleCalendarUpdateParams) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params: GoogleCalendarUpdateParams) => {
|
||||
const updateData: Record<string, unknown> = {}
|
||||
|
||||
if (params.summary !== undefined) {
|
||||
updateData.summary = params.summary
|
||||
}
|
||||
|
||||
if (params.description !== undefined) {
|
||||
updateData.description = params.description
|
||||
}
|
||||
|
||||
if (params.location !== undefined) {
|
||||
updateData.location = params.location
|
||||
}
|
||||
|
||||
if (params.startDateTime !== undefined) {
|
||||
const needsTimezone =
|
||||
!params.startDateTime.includes('+') && !params.startDateTime.includes('-', 10)
|
||||
updateData.start = {
|
||||
dateTime: params.startDateTime,
|
||||
...(needsTimezone && params.timeZone ? { timeZone: params.timeZone } : {}),
|
||||
}
|
||||
}
|
||||
|
||||
if (params.endDateTime !== undefined) {
|
||||
const needsTimezone =
|
||||
!params.endDateTime.includes('+') && !params.endDateTime.includes('-', 10)
|
||||
updateData.end = {
|
||||
dateTime: params.endDateTime,
|
||||
...(needsTimezone && params.timeZone ? { timeZone: params.timeZone } : {}),
|
||||
}
|
||||
}
|
||||
|
||||
// Handle attendees - convert to array format
|
||||
if (params.attendees !== undefined) {
|
||||
let attendeeList: string[] = []
|
||||
const attendees = params.attendees as string | string[]
|
||||
|
||||
if (Array.isArray(attendees)) {
|
||||
attendeeList = attendees.filter((email: string) => email && email.trim().length > 0)
|
||||
} else if (typeof attendees === 'string' && attendees.trim().length > 0) {
|
||||
attendeeList = attendees
|
||||
.split(',')
|
||||
.map((email: string) => email.trim())
|
||||
.filter((email: string) => email.length > 0)
|
||||
}
|
||||
|
||||
updateData.attendees = attendeeList.map((email: string) => ({ email }))
|
||||
}
|
||||
|
||||
return updateData
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data: GoogleCalendarApiEventResponse = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content: `Event "${data.summary}" updated successfully`,
|
||||
metadata: {
|
||||
id: data.id,
|
||||
htmlLink: data.htmlLink,
|
||||
status: data.status,
|
||||
summary: data.summary,
|
||||
description: data.description,
|
||||
location: data.location,
|
||||
start: data.start,
|
||||
end: data.end,
|
||||
attendees: data.attendees,
|
||||
creator: data.creator,
|
||||
organizer: data.organizer,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Event update confirmation message' },
|
||||
metadata: {
|
||||
type: 'json',
|
||||
description: 'Updated event metadata including ID, status, and details',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
interface GoogleCalendarUpdateV2Response {
|
||||
success: boolean
|
||||
output: {
|
||||
id: string
|
||||
htmlLink: string
|
||||
status: string
|
||||
summary: string | null
|
||||
description: string | null
|
||||
location: string | null
|
||||
start: any
|
||||
end: any
|
||||
attendees: any | null
|
||||
creator: any
|
||||
organizer: any
|
||||
}
|
||||
}
|
||||
|
||||
export const updateV2Tool: ToolConfig<GoogleCalendarUpdateParams, GoogleCalendarUpdateV2Response> =
|
||||
{
|
||||
id: 'google_calendar_update_v2',
|
||||
name: 'Google Calendar Update Event',
|
||||
description: 'Update an existing event in Google Calendar. Returns API-aligned fields only.',
|
||||
version: '2.0.0',
|
||||
oauth: updateTool.oauth,
|
||||
params: updateTool.params,
|
||||
request: updateTool.request,
|
||||
transformResponse: async (response: Response) => {
|
||||
const data: GoogleCalendarApiEventResponse = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
htmlLink: data.htmlLink,
|
||||
status: data.status,
|
||||
summary: data.summary ?? null,
|
||||
description: data.description ?? null,
|
||||
location: data.location ?? null,
|
||||
start: data.start,
|
||||
end: data.end,
|
||||
attendees: data.attendees ?? null,
|
||||
creator: data.creator,
|
||||
organizer: data.organizer,
|
||||
},
|
||||
}
|
||||
},
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Event ID' },
|
||||
htmlLink: { type: 'string', description: 'Event link' },
|
||||
status: { type: 'string', description: 'Event status' },
|
||||
summary: { type: 'string', description: 'Event title', optional: true },
|
||||
description: { type: 'string', description: 'Event description', optional: true },
|
||||
location: { type: 'string', description: 'Event location', optional: true },
|
||||
start: { type: 'json', description: 'Event start' },
|
||||
end: { type: 'json', description: 'Event end' },
|
||||
attendees: { type: 'json', description: 'Event attendees', optional: true },
|
||||
creator: { type: 'json', description: 'Event creator' },
|
||||
organizer: { type: 'json', description: 'Event organizer' },
|
||||
},
|
||||
}
|
||||
111
apps/sim/tools/google_drive/copy.ts
Normal file
111
apps/sim/tools/google_drive/copy.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import type { GoogleDriveFile, GoogleDriveToolParams } from '@/tools/google_drive/types'
|
||||
import { ALL_FILE_FIELDS } from '@/tools/google_drive/utils'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
interface GoogleDriveCopyParams extends GoogleDriveToolParams {
|
||||
fileId: string
|
||||
newName?: string
|
||||
destinationFolderId?: string
|
||||
}
|
||||
|
||||
interface GoogleDriveCopyResponse extends ToolResponse {
|
||||
output: {
|
||||
file: GoogleDriveFile
|
||||
}
|
||||
}
|
||||
|
||||
export const copyTool: ToolConfig<GoogleDriveCopyParams, GoogleDriveCopyResponse> = {
|
||||
id: 'google_drive_copy',
|
||||
name: 'Copy Google Drive File',
|
||||
description: 'Create a copy of a file in Google Drive',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-drive',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the file to copy',
|
||||
},
|
||||
newName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Name for the copied file (defaults to "Copy of [original name]")',
|
||||
},
|
||||
destinationFolderId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the folder to place the copy in (defaults to same location as original)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`https://www.googleapis.com/drive/v3/files/${params.fileId?.trim()}/copy`)
|
||||
url.searchParams.append('fields', ALL_FILE_FIELDS)
|
||||
url.searchParams.append('supportsAllDrives', 'true')
|
||||
return url.toString()
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {}
|
||||
if (params.newName) {
|
||||
body.name = params.newName
|
||||
}
|
||||
if (params.destinationFolderId) {
|
||||
body.parents = [params.destinationFolderId.trim()]
|
||||
}
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error?.message || 'Failed to copy Google Drive file')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
file: data,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
file: {
|
||||
type: 'json',
|
||||
description: 'The copied file metadata',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Google Drive file ID of the copy' },
|
||||
name: { type: 'string', description: 'File name' },
|
||||
mimeType: { type: 'string', description: 'MIME type' },
|
||||
webViewLink: { type: 'string', description: 'URL to view in browser' },
|
||||
parents: { type: 'json', description: 'Parent folder IDs' },
|
||||
createdTime: { type: 'string', description: 'File creation time' },
|
||||
modifiedTime: { type: 'string', description: 'Last modification time' },
|
||||
owners: { type: 'json', description: 'List of file owners' },
|
||||
size: { type: 'string', description: 'File size in bytes' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
78
apps/sim/tools/google_drive/delete.ts
Normal file
78
apps/sim/tools/google_drive/delete.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import type { GoogleDriveToolParams } from '@/tools/google_drive/types'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
interface GoogleDriveDeleteParams extends GoogleDriveToolParams {
|
||||
fileId: string
|
||||
}
|
||||
|
||||
interface GoogleDriveDeleteResponse extends ToolResponse {
|
||||
output: {
|
||||
deleted: boolean
|
||||
fileId: string
|
||||
}
|
||||
}
|
||||
|
||||
export const deleteTool: ToolConfig<GoogleDriveDeleteParams, GoogleDriveDeleteResponse> = {
|
||||
id: 'google_drive_delete',
|
||||
name: 'Delete Google Drive File',
|
||||
description: 'Permanently delete a file from Google Drive (bypasses trash)',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-drive',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the file to permanently delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`https://www.googleapis.com/drive/v3/files/${params.fileId?.trim()}`)
|
||||
url.searchParams.append('supportsAllDrives', 'true')
|
||||
return url.toString()
|
||||
},
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params) => {
|
||||
if (!response.ok) {
|
||||
const data = await response.json()
|
||||
throw new Error(data.error?.message || 'Failed to delete Google Drive file')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
deleted: true,
|
||||
fileId: params?.fileId ?? '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
deleted: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the file was successfully deleted',
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
description: 'The ID of the deleted file',
|
||||
},
|
||||
},
|
||||
}
|
||||
137
apps/sim/tools/google_drive/get_about.ts
Normal file
137
apps/sim/tools/google_drive/get_about.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import type { GoogleDriveToolParams, GoogleDriveUser } from '@/tools/google_drive/types'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
interface GoogleDriveGetAboutParams extends GoogleDriveToolParams {}
|
||||
|
||||
interface GoogleDriveGetAboutResponse extends ToolResponse {
|
||||
output: {
|
||||
user: GoogleDriveUser & {
|
||||
emailAddress: string
|
||||
}
|
||||
storageQuota: {
|
||||
limit: string | null
|
||||
usage: string
|
||||
usageInDrive: string
|
||||
usageInDriveTrash: string
|
||||
}
|
||||
canCreateDrives: boolean
|
||||
importFormats: Record<string, string[]>
|
||||
exportFormats: Record<string, string[]>
|
||||
maxUploadSize: string
|
||||
}
|
||||
}
|
||||
|
||||
export const getAboutTool: ToolConfig<GoogleDriveGetAboutParams, GoogleDriveGetAboutResponse> = {
|
||||
id: 'google_drive_get_about',
|
||||
name: 'Get Google Drive Info',
|
||||
description:
|
||||
'Get information about the user and their Google Drive (storage quota, capabilities)',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-drive',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => {
|
||||
const url = new URL('https://www.googleapis.com/drive/v3/about')
|
||||
url.searchParams.append(
|
||||
'fields',
|
||||
'user,storageQuota,canCreateDrives,importFormats,exportFormats,maxUploadSize'
|
||||
)
|
||||
return url.toString()
|
||||
},
|
||||
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 Google Drive info')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
user: {
|
||||
displayName: data.user?.displayName ?? null,
|
||||
emailAddress: data.user?.emailAddress ?? '',
|
||||
photoLink: data.user?.photoLink ?? null,
|
||||
permissionId: data.user?.permissionId ?? null,
|
||||
me: data.user?.me ?? true,
|
||||
},
|
||||
storageQuota: {
|
||||
limit: data.storageQuota?.limit ?? null,
|
||||
usage: data.storageQuota?.usage ?? '0',
|
||||
usageInDrive: data.storageQuota?.usageInDrive ?? '0',
|
||||
usageInDriveTrash: data.storageQuota?.usageInDriveTrash ?? '0',
|
||||
},
|
||||
canCreateDrives: data.canCreateDrives ?? false,
|
||||
importFormats: data.importFormats ?? {},
|
||||
exportFormats: data.exportFormats ?? {},
|
||||
maxUploadSize: data.maxUploadSize ?? '0',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
user: {
|
||||
type: 'json',
|
||||
description: 'Information about the authenticated user',
|
||||
properties: {
|
||||
displayName: { type: 'string', description: 'User display name' },
|
||||
emailAddress: { type: 'string', description: 'User email address' },
|
||||
photoLink: { type: 'string', description: 'URL to user profile photo', optional: true },
|
||||
permissionId: { type: 'string', description: 'User permission ID' },
|
||||
me: { type: 'boolean', description: 'Whether this is the authenticated user' },
|
||||
},
|
||||
},
|
||||
storageQuota: {
|
||||
type: 'json',
|
||||
description: 'Storage quota information in bytes',
|
||||
properties: {
|
||||
limit: {
|
||||
type: 'string',
|
||||
description: 'Total storage limit in bytes (null for unlimited)',
|
||||
optional: true,
|
||||
},
|
||||
usage: { type: 'string', description: 'Total storage used in bytes' },
|
||||
usageInDrive: { type: 'string', description: 'Storage used by Drive files in bytes' },
|
||||
usageInDriveTrash: {
|
||||
type: 'string',
|
||||
description: 'Storage used by trashed files in bytes',
|
||||
},
|
||||
},
|
||||
},
|
||||
canCreateDrives: {
|
||||
type: 'boolean',
|
||||
description: 'Whether user can create shared drives',
|
||||
},
|
||||
importFormats: {
|
||||
type: 'json',
|
||||
description: 'Map of MIME types that can be imported and their target formats',
|
||||
},
|
||||
exportFormats: {
|
||||
type: 'json',
|
||||
description: 'Map of Google Workspace MIME types and their exportable formats',
|
||||
},
|
||||
maxUploadSize: {
|
||||
type: 'string',
|
||||
description: 'Maximum upload size in bytes',
|
||||
},
|
||||
},
|
||||
}
|
||||
99
apps/sim/tools/google_drive/get_file.ts
Normal file
99
apps/sim/tools/google_drive/get_file.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import type { GoogleDriveFile, GoogleDriveToolParams } from '@/tools/google_drive/types'
|
||||
import { ALL_FILE_FIELDS } from '@/tools/google_drive/utils'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
interface GoogleDriveGetFileParams extends GoogleDriveToolParams {
|
||||
fileId: string
|
||||
}
|
||||
|
||||
interface GoogleDriveGetFileResponse extends ToolResponse {
|
||||
output: {
|
||||
file: GoogleDriveFile
|
||||
}
|
||||
}
|
||||
|
||||
export const getFileTool: ToolConfig<GoogleDriveGetFileParams, GoogleDriveGetFileResponse> = {
|
||||
id: 'google_drive_get_file',
|
||||
name: 'Get Google Drive File',
|
||||
description: 'Get metadata for a specific file in Google Drive by its ID',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-drive',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the file to retrieve',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`https://www.googleapis.com/drive/v3/files/${params.fileId?.trim()}`)
|
||||
url.searchParams.append('fields', ALL_FILE_FIELDS)
|
||||
url.searchParams.append('supportsAllDrives', 'true')
|
||||
return url.toString()
|
||||
},
|
||||
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 Google Drive file')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
file: data,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
file: {
|
||||
type: 'json',
|
||||
description: 'The file metadata',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Google Drive file ID' },
|
||||
name: { type: 'string', description: 'File name' },
|
||||
mimeType: { type: 'string', description: 'MIME type' },
|
||||
description: { type: 'string', description: 'File description', optional: true },
|
||||
size: { type: 'string', description: 'File size in bytes', optional: true },
|
||||
starred: { type: 'boolean', description: 'Whether file is starred' },
|
||||
trashed: { type: 'boolean', description: 'Whether file is in trash' },
|
||||
webViewLink: { type: 'string', description: 'URL to view in browser' },
|
||||
webContentLink: { type: 'string', description: 'Direct download URL', optional: true },
|
||||
iconLink: { type: 'string', description: 'URL to file icon' },
|
||||
thumbnailLink: { type: 'string', description: 'URL to thumbnail', optional: true },
|
||||
parents: { type: 'json', description: 'Parent folder IDs' },
|
||||
owners: { type: 'json', description: 'List of file owners' },
|
||||
permissions: { type: 'json', description: 'File permissions', optional: true },
|
||||
createdTime: { type: 'string', description: 'File creation time' },
|
||||
modifiedTime: { type: 'string', description: 'Last modification time' },
|
||||
lastModifyingUser: { type: 'json', description: 'User who last modified the file' },
|
||||
shared: { type: 'boolean', description: 'Whether file is shared' },
|
||||
ownedByMe: { type: 'boolean', description: 'Whether owned by current user' },
|
||||
capabilities: { type: 'json', description: 'User capabilities on file' },
|
||||
md5Checksum: { type: 'string', description: 'MD5 hash', optional: true },
|
||||
version: { type: 'string', description: 'Version number' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,11 +1,31 @@
|
||||
import { copyTool } from '@/tools/google_drive/copy'
|
||||
import { createFolderTool } from '@/tools/google_drive/create_folder'
|
||||
import { deleteTool } from '@/tools/google_drive/delete'
|
||||
import { downloadTool } from '@/tools/google_drive/download'
|
||||
import { getAboutTool } from '@/tools/google_drive/get_about'
|
||||
import { getContentTool } from '@/tools/google_drive/get_content'
|
||||
import { getFileTool } from '@/tools/google_drive/get_file'
|
||||
import { listTool } from '@/tools/google_drive/list'
|
||||
import { listPermissionsTool } from '@/tools/google_drive/list_permissions'
|
||||
import { shareTool } from '@/tools/google_drive/share'
|
||||
import { trashTool } from '@/tools/google_drive/trash'
|
||||
import { unshareTool } from '@/tools/google_drive/unshare'
|
||||
import { untrashTool } from '@/tools/google_drive/untrash'
|
||||
import { updateTool } from '@/tools/google_drive/update'
|
||||
import { uploadTool } from '@/tools/google_drive/upload'
|
||||
|
||||
export const googleDriveCopyTool = copyTool
|
||||
export const googleDriveCreateFolderTool = createFolderTool
|
||||
export const googleDriveDeleteTool = deleteTool
|
||||
export const googleDriveDownloadTool = downloadTool
|
||||
export const googleDriveGetAboutTool = getAboutTool
|
||||
export const googleDriveGetContentTool = getContentTool
|
||||
export const googleDriveGetFileTool = getFileTool
|
||||
export const googleDriveListTool = listTool
|
||||
export const googleDriveListPermissionsTool = listPermissionsTool
|
||||
export const googleDriveShareTool = shareTool
|
||||
export const googleDriveTrashTool = trashTool
|
||||
export const googleDriveUnshareTool = unshareTool
|
||||
export const googleDriveUntrashTool = untrashTool
|
||||
export const googleDriveUpdateTool = updateTool
|
||||
export const googleDriveUploadTool = uploadTool
|
||||
|
||||
124
apps/sim/tools/google_drive/list_permissions.ts
Normal file
124
apps/sim/tools/google_drive/list_permissions.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import type { GoogleDrivePermission, GoogleDriveToolParams } from '@/tools/google_drive/types'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
interface GoogleDriveListPermissionsParams extends GoogleDriveToolParams {
|
||||
fileId: string
|
||||
}
|
||||
|
||||
interface GoogleDriveListPermissionsResponse extends ToolResponse {
|
||||
output: {
|
||||
permissions: GoogleDrivePermission[]
|
||||
}
|
||||
}
|
||||
|
||||
export const listPermissionsTool: ToolConfig<
|
||||
GoogleDriveListPermissionsParams,
|
||||
GoogleDriveListPermissionsResponse
|
||||
> = {
|
||||
id: 'google_drive_list_permissions',
|
||||
name: 'List Google Drive Permissions',
|
||||
description: 'List all permissions (who has access) for a file in Google Drive',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-drive',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the file to list permissions for',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(
|
||||
`https://www.googleapis.com/drive/v3/files/${params.fileId?.trim()}/permissions`
|
||||
)
|
||||
url.searchParams.append('supportsAllDrives', 'true')
|
||||
url.searchParams.append(
|
||||
'fields',
|
||||
'permissions(id,type,role,emailAddress,displayName,photoLink,domain,expirationTime,deleted,allowFileDiscovery,pendingOwner,permissionDetails)'
|
||||
)
|
||||
return url.toString()
|
||||
},
|
||||
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 Google Drive permissions')
|
||||
}
|
||||
|
||||
const permissions = (data.permissions ?? []).map((p: Record<string, unknown>) => ({
|
||||
id: p.id ?? null,
|
||||
type: p.type ?? null,
|
||||
role: p.role ?? null,
|
||||
emailAddress: p.emailAddress ?? null,
|
||||
displayName: p.displayName ?? null,
|
||||
photoLink: p.photoLink ?? null,
|
||||
domain: p.domain ?? null,
|
||||
expirationTime: p.expirationTime ?? null,
|
||||
deleted: p.deleted ?? false,
|
||||
allowFileDiscovery: p.allowFileDiscovery ?? null,
|
||||
pendingOwner: p.pendingOwner ?? false,
|
||||
permissionDetails: p.permissionDetails ?? null,
|
||||
}))
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
permissions,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
permissions: {
|
||||
type: 'array',
|
||||
description: 'List of permissions on the file',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Permission ID (use to remove permission)' },
|
||||
type: { type: 'string', description: 'Grantee type (user, group, domain, anyone)' },
|
||||
role: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Permission role (owner, organizer, fileOrganizer, writer, commenter, reader)',
|
||||
},
|
||||
emailAddress: { type: 'string', description: 'Email of the grantee' },
|
||||
displayName: { type: 'string', description: 'Display name of the grantee' },
|
||||
photoLink: { type: 'string', description: 'Photo URL of the grantee' },
|
||||
domain: { type: 'string', description: 'Domain of the grantee' },
|
||||
expirationTime: { type: 'string', description: 'When permission expires' },
|
||||
deleted: { type: 'boolean', description: 'Whether grantee account is deleted' },
|
||||
allowFileDiscovery: {
|
||||
type: 'boolean',
|
||||
description: 'Whether file is discoverable by grantee',
|
||||
},
|
||||
pendingOwner: { type: 'boolean', description: 'Whether ownership transfer is pending' },
|
||||
permissionDetails: {
|
||||
type: 'json',
|
||||
description: 'Details about inherited permissions',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
178
apps/sim/tools/google_drive/share.ts
Normal file
178
apps/sim/tools/google_drive/share.ts
Normal file
@@ -0,0 +1,178 @@
|
||||
import type { GoogleDrivePermission, GoogleDriveToolParams } from '@/tools/google_drive/types'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
interface GoogleDriveShareParams extends GoogleDriveToolParams {
|
||||
fileId: string
|
||||
email?: string
|
||||
domain?: string
|
||||
type: 'user' | 'group' | 'domain' | 'anyone'
|
||||
role: 'owner' | 'organizer' | 'fileOrganizer' | 'writer' | 'commenter' | 'reader'
|
||||
transferOwnership?: boolean
|
||||
moveToNewOwnersRoot?: boolean
|
||||
sendNotification?: boolean
|
||||
emailMessage?: string
|
||||
}
|
||||
|
||||
interface GoogleDriveShareResponse extends ToolResponse {
|
||||
output: {
|
||||
permission: GoogleDrivePermission
|
||||
}
|
||||
}
|
||||
|
||||
export const shareTool: ToolConfig<GoogleDriveShareParams, GoogleDriveShareResponse> = {
|
||||
id: 'google_drive_share',
|
||||
name: 'Share Google Drive File',
|
||||
description: 'Share a file with a user, group, domain, or make it public',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-drive',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the file to share',
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Type of grantee: user, group, domain, or anyone',
|
||||
},
|
||||
role: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Permission role: owner (transfer ownership), organizer (shared drive only), fileOrganizer (shared drive only), writer (edit), commenter (view and comment), reader (view only)',
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Email address of the user or group (required for type=user or type=group)',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Domain to share with (required for type=domain)',
|
||||
},
|
||||
transferOwnership: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Required when role is owner. Transfers ownership to the specified user.',
|
||||
},
|
||||
moveToNewOwnersRoot: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
"When transferring ownership, move the file to the new owner's My Drive root folder.",
|
||||
},
|
||||
sendNotification: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to send an email notification (default: true)',
|
||||
},
|
||||
emailMessage: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Custom message to include in the notification email',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(
|
||||
`https://www.googleapis.com/drive/v3/files/${params.fileId?.trim()}/permissions`
|
||||
)
|
||||
url.searchParams.append('supportsAllDrives', 'true')
|
||||
if (params.transferOwnership) {
|
||||
url.searchParams.append('transferOwnership', 'true')
|
||||
}
|
||||
if (params.moveToNewOwnersRoot) {
|
||||
url.searchParams.append('moveToNewOwnersRoot', 'true')
|
||||
}
|
||||
if (params.sendNotification !== undefined) {
|
||||
url.searchParams.append('sendNotificationEmail', String(params.sendNotification))
|
||||
}
|
||||
if (params.emailMessage) {
|
||||
url.searchParams.append('emailMessage', params.emailMessage)
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {
|
||||
type: params.type,
|
||||
role: params.role,
|
||||
}
|
||||
if (params.email) {
|
||||
body.emailAddress = params.email.trim()
|
||||
}
|
||||
if (params.domain) {
|
||||
body.domain = params.domain.trim()
|
||||
}
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error?.message || 'Failed to share Google Drive file')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
permission: {
|
||||
id: data.id ?? null,
|
||||
type: data.type ?? null,
|
||||
role: data.role ?? null,
|
||||
emailAddress: data.emailAddress ?? null,
|
||||
displayName: data.displayName ?? null,
|
||||
domain: data.domain ?? null,
|
||||
expirationTime: data.expirationTime ?? null,
|
||||
deleted: data.deleted ?? false,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
permission: {
|
||||
type: 'json',
|
||||
description: 'The created permission details',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Permission ID' },
|
||||
type: { type: 'string', description: 'Grantee type (user, group, domain, anyone)' },
|
||||
role: { type: 'string', description: 'Permission role' },
|
||||
emailAddress: { type: 'string', description: 'Email of the grantee', optional: true },
|
||||
displayName: { type: 'string', description: 'Display name of the grantee', optional: true },
|
||||
domain: { type: 'string', description: 'Domain of the grantee', optional: true },
|
||||
expirationTime: { type: 'string', description: 'Expiration time', optional: true },
|
||||
deleted: { type: 'boolean', description: 'Whether grantee is deleted' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
87
apps/sim/tools/google_drive/trash.ts
Normal file
87
apps/sim/tools/google_drive/trash.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import type { GoogleDriveFile, GoogleDriveToolParams } from '@/tools/google_drive/types'
|
||||
import { ALL_FILE_FIELDS } from '@/tools/google_drive/utils'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
interface GoogleDriveTrashParams extends GoogleDriveToolParams {
|
||||
fileId: string
|
||||
}
|
||||
|
||||
interface GoogleDriveTrashResponse extends ToolResponse {
|
||||
output: {
|
||||
file: GoogleDriveFile
|
||||
}
|
||||
}
|
||||
|
||||
export const trashTool: ToolConfig<GoogleDriveTrashParams, GoogleDriveTrashResponse> = {
|
||||
id: 'google_drive_trash',
|
||||
name: 'Trash Google Drive File',
|
||||
description: 'Move a file to the trash in Google Drive (can be restored later)',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-drive',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the file to move to trash',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`https://www.googleapis.com/drive/v3/files/${params.fileId?.trim()}`)
|
||||
url.searchParams.append('fields', ALL_FILE_FIELDS)
|
||||
url.searchParams.append('supportsAllDrives', 'true')
|
||||
return url.toString()
|
||||
},
|
||||
method: 'PATCH',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: () => ({
|
||||
trashed: true,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error?.message || 'Failed to trash Google Drive file')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
file: data,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
file: {
|
||||
type: 'json',
|
||||
description: 'The trashed file metadata',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Google Drive file ID' },
|
||||
name: { type: 'string', description: 'File name' },
|
||||
mimeType: { type: 'string', description: 'MIME type' },
|
||||
trashed: { type: 'boolean', description: 'Whether file is in trash (should be true)' },
|
||||
trashedTime: { type: 'string', description: 'When file was trashed' },
|
||||
webViewLink: { type: 'string', description: 'URL to view in browser' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
93
apps/sim/tools/google_drive/unshare.ts
Normal file
93
apps/sim/tools/google_drive/unshare.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import type { GoogleDriveToolParams } from '@/tools/google_drive/types'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
interface GoogleDriveUnshareParams extends GoogleDriveToolParams {
|
||||
fileId: string
|
||||
permissionId: string
|
||||
}
|
||||
|
||||
interface GoogleDriveUnshareResponse extends ToolResponse {
|
||||
output: {
|
||||
removed: boolean
|
||||
fileId: string
|
||||
permissionId: string
|
||||
}
|
||||
}
|
||||
|
||||
export const unshareTool: ToolConfig<GoogleDriveUnshareParams, GoogleDriveUnshareResponse> = {
|
||||
id: 'google_drive_unshare',
|
||||
name: 'Unshare Google Drive File',
|
||||
description: 'Remove a permission from a file (revoke access)',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-drive',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the file to modify permissions on',
|
||||
},
|
||||
permissionId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the permission to remove (use list_permissions to find this)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(
|
||||
`https://www.googleapis.com/drive/v3/files/${params.fileId?.trim()}/permissions/${params.permissionId?.trim()}`
|
||||
)
|
||||
url.searchParams.append('supportsAllDrives', 'true')
|
||||
return url.toString()
|
||||
},
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params) => {
|
||||
if (!response.ok) {
|
||||
const data = await response.json()
|
||||
throw new Error(data.error?.message || 'Failed to remove permission from Google Drive file')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
removed: true,
|
||||
fileId: params?.fileId ?? '',
|
||||
permissionId: params?.permissionId ?? '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
removed: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the permission was successfully removed',
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
description: 'The ID of the file',
|
||||
},
|
||||
permissionId: {
|
||||
type: 'string',
|
||||
description: 'The ID of the removed permission',
|
||||
},
|
||||
},
|
||||
}
|
||||
87
apps/sim/tools/google_drive/untrash.ts
Normal file
87
apps/sim/tools/google_drive/untrash.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import type { GoogleDriveFile, GoogleDriveToolParams } from '@/tools/google_drive/types'
|
||||
import { ALL_FILE_FIELDS } from '@/tools/google_drive/utils'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
interface GoogleDriveUntrashParams extends GoogleDriveToolParams {
|
||||
fileId: string
|
||||
}
|
||||
|
||||
interface GoogleDriveUntrashResponse extends ToolResponse {
|
||||
output: {
|
||||
file: GoogleDriveFile
|
||||
}
|
||||
}
|
||||
|
||||
export const untrashTool: ToolConfig<GoogleDriveUntrashParams, GoogleDriveUntrashResponse> = {
|
||||
id: 'google_drive_untrash',
|
||||
name: 'Restore Google Drive File',
|
||||
description: 'Restore a file from the trash in Google Drive',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-drive',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the file to restore from trash',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`https://www.googleapis.com/drive/v3/files/${params.fileId?.trim()}`)
|
||||
url.searchParams.append('fields', ALL_FILE_FIELDS)
|
||||
url.searchParams.append('supportsAllDrives', 'true')
|
||||
return url.toString()
|
||||
},
|
||||
method: 'PATCH',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: () => ({
|
||||
trashed: false,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error?.message || 'Failed to restore Google Drive file from trash')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
file: data,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
file: {
|
||||
type: 'json',
|
||||
description: 'The restored file metadata',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Google Drive file ID' },
|
||||
name: { type: 'string', description: 'File name' },
|
||||
mimeType: { type: 'string', description: 'MIME type' },
|
||||
trashed: { type: 'boolean', description: 'Whether file is in trash (should be false)' },
|
||||
webViewLink: { type: 'string', description: 'URL to view in browser' },
|
||||
parents: { type: 'json', description: 'Parent folder IDs' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
140
apps/sim/tools/google_drive/update.ts
Normal file
140
apps/sim/tools/google_drive/update.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import type { GoogleDriveFile, GoogleDriveToolParams } from '@/tools/google_drive/types'
|
||||
import { ALL_FILE_FIELDS } from '@/tools/google_drive/utils'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
interface GoogleDriveUpdateParams extends GoogleDriveToolParams {
|
||||
fileId: string
|
||||
name?: string
|
||||
description?: string
|
||||
addParents?: string
|
||||
removeParents?: string
|
||||
starred?: boolean
|
||||
}
|
||||
|
||||
interface GoogleDriveUpdateResponse extends ToolResponse {
|
||||
output: {
|
||||
file: GoogleDriveFile
|
||||
}
|
||||
}
|
||||
|
||||
export const updateTool: ToolConfig<GoogleDriveUpdateParams, GoogleDriveUpdateResponse> = {
|
||||
id: 'google_drive_update',
|
||||
name: 'Update Google Drive File',
|
||||
description: 'Update file metadata in Google Drive (rename, move, star, add description)',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-drive',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the file to update',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New name for the file',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New description for the file',
|
||||
},
|
||||
addParents: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of parent folder IDs to add (moves file to these folders)',
|
||||
},
|
||||
removeParents: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of parent folder IDs to remove',
|
||||
},
|
||||
starred: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to star or unstar the file',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`https://www.googleapis.com/drive/v3/files/${params.fileId?.trim()}`)
|
||||
url.searchParams.append('fields', ALL_FILE_FIELDS)
|
||||
url.searchParams.append('supportsAllDrives', 'true')
|
||||
if (params.addParents) {
|
||||
url.searchParams.append('addParents', params.addParents.trim())
|
||||
}
|
||||
if (params.removeParents) {
|
||||
url.searchParams.append('removeParents', params.removeParents.trim())
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'PATCH',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {}
|
||||
if (params.name !== undefined) {
|
||||
body.name = params.name
|
||||
}
|
||||
if (params.description !== undefined) {
|
||||
body.description = params.description
|
||||
}
|
||||
if (params.starred !== undefined) {
|
||||
body.starred = params.starred
|
||||
}
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error?.message || 'Failed to update Google Drive file')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
file: data,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
file: {
|
||||
type: 'json',
|
||||
description: 'The updated file metadata',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Google Drive file ID' },
|
||||
name: { type: 'string', description: 'File name' },
|
||||
mimeType: { type: 'string', description: 'MIME type' },
|
||||
description: { type: 'string', description: 'File description', optional: true },
|
||||
starred: { type: 'boolean', description: 'Whether file is starred' },
|
||||
webViewLink: { type: 'string', description: 'URL to view in browser' },
|
||||
parents: { type: 'json', description: 'Parent folder IDs' },
|
||||
modifiedTime: { type: 'string', description: 'Last modification time' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
import { getResponsesTool } from '@/tools/google_form/get_responses'
|
||||
|
||||
export const googleFormsGetResponsesTool = getResponsesTool
|
||||
@@ -1,21 +0,0 @@
|
||||
export interface GoogleFormsResponse {
|
||||
responseId?: string
|
||||
createTime?: string
|
||||
lastSubmittedTime?: string
|
||||
answers?: Record<string, unknown>
|
||||
respondentEmail?: string
|
||||
totalScore?: number
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
export interface GoogleFormsResponseList {
|
||||
responses?: GoogleFormsResponse[]
|
||||
nextPageToken?: string
|
||||
}
|
||||
|
||||
export interface GoogleFormsGetResponsesParams {
|
||||
accessToken: string
|
||||
formId: string
|
||||
responseId?: string
|
||||
pageSize?: number
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
|
||||
export const FORMS_API_BASE = 'https://forms.googleapis.com/v1'
|
||||
|
||||
const logger = createLogger('GoogleFormsUtils')
|
||||
|
||||
export function buildListResponsesUrl(params: { formId: string; pageSize?: number }): string {
|
||||
const { formId, pageSize } = params
|
||||
const url = new URL(`${FORMS_API_BASE}/forms/${encodeURIComponent(formId)}/responses`)
|
||||
if (pageSize && pageSize > 0) {
|
||||
const limited = Math.min(pageSize, 5000)
|
||||
url.searchParams.set('pageSize', String(limited))
|
||||
}
|
||||
const finalUrl = url.toString()
|
||||
logger.debug('Built Google Forms list responses URL', { finalUrl })
|
||||
return finalUrl
|
||||
}
|
||||
|
||||
export function buildGetResponseUrl(params: { formId: string; responseId: string }): string {
|
||||
const { formId, responseId } = params
|
||||
const finalUrl = `${FORMS_API_BASE}/forms/${encodeURIComponent(formId)}/responses/${encodeURIComponent(responseId)}`
|
||||
logger.debug('Built Google Forms get response URL', { finalUrl })
|
||||
return finalUrl
|
||||
}
|
||||
118
apps/sim/tools/google_forms/batch_update.ts
Normal file
118
apps/sim/tools/google_forms/batch_update.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import type {
|
||||
GoogleForm,
|
||||
GoogleFormsBatchUpdateParams,
|
||||
GoogleFormsBatchUpdateResponse,
|
||||
} from '@/tools/google_forms/types'
|
||||
import { buildBatchUpdateUrl } from '@/tools/google_forms/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface BatchUpdateApiResponse {
|
||||
replies?: Record<string, unknown>[]
|
||||
writeControl?: {
|
||||
requiredRevisionId?: string
|
||||
targetRevisionId?: string
|
||||
}
|
||||
form?: GoogleForm
|
||||
}
|
||||
|
||||
export const batchUpdateTool: ToolConfig<
|
||||
GoogleFormsBatchUpdateParams,
|
||||
GoogleFormsBatchUpdateResponse
|
||||
> = {
|
||||
id: 'google_forms_batch_update',
|
||||
name: 'Google Forms: Batch Update',
|
||||
description: 'Apply multiple updates to a form (add items, update info, change settings, etc.)',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-forms',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
formId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'The ID of the Google Form to update',
|
||||
},
|
||||
requests: {
|
||||
type: 'json',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Array of update requests (updateFormInfo, updateSettings, createItem, updateItem, moveItem, deleteItem)',
|
||||
},
|
||||
includeFormInResponse: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to return the updated form in the response',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: GoogleFormsBatchUpdateParams) => buildBatchUpdateUrl(params.formId),
|
||||
method: 'POST',
|
||||
headers: (params: GoogleFormsBatchUpdateParams) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params: GoogleFormsBatchUpdateParams) => ({
|
||||
requests: params.requests,
|
||||
includeFormInResponse: params.includeFormInResponse ?? false,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = (await response.json()) as BatchUpdateApiResponse
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = data as unknown as { error?: { message?: string } }
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
replies: [],
|
||||
writeControl: null,
|
||||
form: null,
|
||||
},
|
||||
error: errorData.error?.message ?? 'Failed to batch update form',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
replies: data.replies ?? [],
|
||||
writeControl: data.writeControl ?? null,
|
||||
form: data.form ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
replies: {
|
||||
type: 'array',
|
||||
description: 'The replies from each update request',
|
||||
items: {
|
||||
type: 'json',
|
||||
},
|
||||
},
|
||||
writeControl: {
|
||||
type: 'json',
|
||||
description: 'Write control information with revision IDs',
|
||||
optional: true,
|
||||
},
|
||||
form: {
|
||||
type: 'json',
|
||||
description: 'The updated form (if includeFormInResponse was true)',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
106
apps/sim/tools/google_forms/create_form.ts
Normal file
106
apps/sim/tools/google_forms/create_form.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import type {
|
||||
GoogleForm,
|
||||
GoogleFormsCreateFormParams,
|
||||
GoogleFormsCreateFormResponse,
|
||||
} from '@/tools/google_forms/types'
|
||||
import { buildCreateFormUrl } from '@/tools/google_forms/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const createFormTool: ToolConfig<
|
||||
GoogleFormsCreateFormParams,
|
||||
GoogleFormsCreateFormResponse
|
||||
> = {
|
||||
id: 'google_forms_create_form',
|
||||
name: 'Google Forms: Create Form',
|
||||
description: 'Create a new Google Form with a title',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-forms',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The title of the form visible to responders',
|
||||
},
|
||||
documentTitle: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The document title visible in Drive (defaults to form title)',
|
||||
},
|
||||
unpublished: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'If true, create an unpublished form that does not accept responses',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: GoogleFormsCreateFormParams) => buildCreateFormUrl(params.unpublished),
|
||||
method: 'POST',
|
||||
headers: (params: GoogleFormsCreateFormParams) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params: GoogleFormsCreateFormParams) => ({
|
||||
info: {
|
||||
title: params.title,
|
||||
...(params.documentTitle ? { documentTitle: params.documentTitle } : {}),
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = (await response.json()) as GoogleForm
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = data as unknown as { error?: { message?: string } }
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
formId: '',
|
||||
title: null,
|
||||
documentTitle: null,
|
||||
responderUri: null,
|
||||
revisionId: null,
|
||||
},
|
||||
error: errorData.error?.message ?? 'Failed to create form',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
formId: data.formId ?? '',
|
||||
title: data.info?.title ?? null,
|
||||
documentTitle: data.info?.documentTitle ?? null,
|
||||
responderUri: data.responderUri ?? null,
|
||||
revisionId: data.revisionId ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
formId: { type: 'string', description: 'The ID of the created form' },
|
||||
title: { type: 'string', description: 'The form title', optional: true },
|
||||
documentTitle: { type: 'string', description: 'The document title in Drive', optional: true },
|
||||
responderUri: {
|
||||
type: 'string',
|
||||
description: 'The URI to share with responders',
|
||||
optional: true,
|
||||
},
|
||||
revisionId: { type: 'string', description: 'The revision ID of the form', optional: true },
|
||||
},
|
||||
}
|
||||
120
apps/sim/tools/google_forms/create_watch.ts
Normal file
120
apps/sim/tools/google_forms/create_watch.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import type {
|
||||
GoogleFormsCreateWatchParams,
|
||||
GoogleFormsCreateWatchResponse,
|
||||
GoogleFormsWatch,
|
||||
} from '@/tools/google_forms/types'
|
||||
import { buildCreateWatchUrl } from '@/tools/google_forms/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const createWatchTool: ToolConfig<
|
||||
GoogleFormsCreateWatchParams,
|
||||
GoogleFormsCreateWatchResponse
|
||||
> = {
|
||||
id: 'google_forms_create_watch',
|
||||
name: 'Google Forms: Create Watch',
|
||||
description: 'Create a notification watch for form changes (schema changes or new responses)',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-forms',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
formId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'The ID of the Google Form to watch',
|
||||
},
|
||||
eventType: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Event type to watch: SCHEMA (form changes) or RESPONSES (new submissions)',
|
||||
},
|
||||
topicName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'The Cloud Pub/Sub topic name (format: projects/{project}/topics/{topic})',
|
||||
},
|
||||
watchId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Custom watch ID (4-63 chars, lowercase letters, numbers, hyphens)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: GoogleFormsCreateWatchParams) => buildCreateWatchUrl(params.formId),
|
||||
method: 'POST',
|
||||
headers: (params: GoogleFormsCreateWatchParams) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params: GoogleFormsCreateWatchParams) => ({
|
||||
watch: {
|
||||
target: {
|
||||
topic: {
|
||||
topicName: params.topicName,
|
||||
},
|
||||
},
|
||||
eventType: params.eventType,
|
||||
},
|
||||
...(params.watchId ? { watchId: params.watchId } : {}),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = (await response.json()) as GoogleFormsWatch
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = data as unknown as { error?: { message?: string } }
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
id: '',
|
||||
eventType: '',
|
||||
topicName: null,
|
||||
createTime: null,
|
||||
expireTime: null,
|
||||
state: null,
|
||||
},
|
||||
error: errorData.error?.message ?? 'Failed to create watch',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? '',
|
||||
eventType: data.eventType ?? '',
|
||||
topicName: data.target?.topic?.topicName ?? null,
|
||||
createTime: data.createTime ?? null,
|
||||
expireTime: data.expireTime ?? null,
|
||||
state: data.state ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'The watch ID' },
|
||||
eventType: { type: 'string', description: 'The event type being watched' },
|
||||
topicName: { type: 'string', description: 'The Cloud Pub/Sub topic', optional: true },
|
||||
createTime: { type: 'string', description: 'When the watch was created', optional: true },
|
||||
expireTime: {
|
||||
type: 'string',
|
||||
description: 'When the watch expires (7 days after creation)',
|
||||
optional: true,
|
||||
},
|
||||
state: { type: 'string', description: 'The watch state (ACTIVE, SUSPENDED)', optional: true },
|
||||
},
|
||||
}
|
||||
76
apps/sim/tools/google_forms/delete_watch.ts
Normal file
76
apps/sim/tools/google_forms/delete_watch.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import type {
|
||||
GoogleFormsDeleteWatchParams,
|
||||
GoogleFormsDeleteWatchResponse,
|
||||
} from '@/tools/google_forms/types'
|
||||
import { buildDeleteWatchUrl } from '@/tools/google_forms/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const deleteWatchTool: ToolConfig<
|
||||
GoogleFormsDeleteWatchParams,
|
||||
GoogleFormsDeleteWatchResponse
|
||||
> = {
|
||||
id: 'google_forms_delete_watch',
|
||||
name: 'Google Forms: Delete Watch',
|
||||
description: 'Delete a notification watch from a form',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-forms',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
formId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'The ID of the Google Form',
|
||||
},
|
||||
watchId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'The ID of the watch to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: GoogleFormsDeleteWatchParams) =>
|
||||
buildDeleteWatchUrl(params.formId, params.watchId),
|
||||
method: 'DELETE',
|
||||
headers: (params: GoogleFormsDeleteWatchParams) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const data = (await response.json()) as { error?: { message?: string } }
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
deleted: false,
|
||||
},
|
||||
error: data.error?.message ?? 'Failed to delete watch',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
deleted: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
deleted: { type: 'boolean', description: 'Whether the watch was successfully deleted' },
|
||||
},
|
||||
}
|
||||
119
apps/sim/tools/google_forms/get_form.ts
Normal file
119
apps/sim/tools/google_forms/get_form.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import type {
|
||||
GoogleForm,
|
||||
GoogleFormsGetFormParams,
|
||||
GoogleFormsGetFormResponse,
|
||||
} from '@/tools/google_forms/types'
|
||||
import { buildGetFormUrl } from '@/tools/google_forms/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const getFormTool: ToolConfig<GoogleFormsGetFormParams, GoogleFormsGetFormResponse> = {
|
||||
id: 'google_forms_get_form',
|
||||
name: 'Google Forms: Get Form',
|
||||
description: 'Retrieve a form structure including its items, settings, and metadata',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-forms',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
formId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'The ID of the Google Form to retrieve',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: GoogleFormsGetFormParams) => buildGetFormUrl(params.formId),
|
||||
method: 'GET',
|
||||
headers: (params: GoogleFormsGetFormParams) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = (await response.json()) as GoogleForm
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = data as unknown as { error?: { message?: string } }
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
formId: '',
|
||||
title: null,
|
||||
description: null,
|
||||
documentTitle: null,
|
||||
responderUri: null,
|
||||
linkedSheetId: null,
|
||||
revisionId: null,
|
||||
items: [],
|
||||
settings: null,
|
||||
publishSettings: null,
|
||||
},
|
||||
error: errorData.error?.message ?? 'Failed to get form',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
formId: data.formId ?? '',
|
||||
title: data.info?.title ?? null,
|
||||
description: data.info?.description ?? null,
|
||||
documentTitle: data.info?.documentTitle ?? null,
|
||||
responderUri: data.responderUri ?? null,
|
||||
linkedSheetId: data.linkedSheetId ?? null,
|
||||
revisionId: data.revisionId ?? null,
|
||||
items: data.items ?? [],
|
||||
settings: data.settings ?? null,
|
||||
publishSettings: data.publishSettings ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
formId: { type: 'string', description: 'The form ID' },
|
||||
title: { type: 'string', description: 'The form title visible to responders', optional: true },
|
||||
description: { type: 'string', description: 'The form description', optional: true },
|
||||
documentTitle: {
|
||||
type: 'string',
|
||||
description: 'The document title visible in Drive',
|
||||
optional: true,
|
||||
},
|
||||
responderUri: {
|
||||
type: 'string',
|
||||
description: 'The URI to share with responders',
|
||||
optional: true,
|
||||
},
|
||||
linkedSheetId: {
|
||||
type: 'string',
|
||||
description: 'The ID of the linked Google Sheet',
|
||||
optional: true,
|
||||
},
|
||||
revisionId: { type: 'string', description: 'The revision ID of the form', optional: true },
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'The form items (questions, sections, etc.)',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
itemId: { type: 'string', description: 'Item ID' },
|
||||
title: { type: 'string', description: 'Item title' },
|
||||
description: { type: 'string', description: 'Item description' },
|
||||
},
|
||||
},
|
||||
},
|
||||
settings: { type: 'json', description: 'Form settings', optional: true },
|
||||
publishSettings: { type: 'json', description: 'Form publish settings', optional: true },
|
||||
},
|
||||
}
|
||||
@@ -2,8 +2,8 @@ import type {
|
||||
GoogleFormsGetResponsesParams,
|
||||
GoogleFormsResponse,
|
||||
GoogleFormsResponseList,
|
||||
} from '@/tools/google_form/types'
|
||||
import { buildGetResponseUrl, buildListResponsesUrl } from '@/tools/google_form/utils'
|
||||
} from '@/tools/google_forms/types'
|
||||
import { buildGetResponseUrl, buildListResponsesUrl } from '@/tools/google_forms/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const getResponsesTool: ToolConfig<GoogleFormsGetResponsesParams> = {
|
||||
21
apps/sim/tools/google_forms/index.ts
Normal file
21
apps/sim/tools/google_forms/index.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { batchUpdateTool } from '@/tools/google_forms/batch_update'
|
||||
import { createFormTool } from '@/tools/google_forms/create_form'
|
||||
import { createWatchTool } from '@/tools/google_forms/create_watch'
|
||||
import { deleteWatchTool } from '@/tools/google_forms/delete_watch'
|
||||
import { getFormTool } from '@/tools/google_forms/get_form'
|
||||
import { getResponsesTool } from '@/tools/google_forms/get_responses'
|
||||
import { listWatchesTool } from '@/tools/google_forms/list_watches'
|
||||
import { renewWatchTool } from '@/tools/google_forms/renew_watch'
|
||||
import { setPublishSettingsTool } from '@/tools/google_forms/set_publish_settings'
|
||||
|
||||
export const googleFormsGetResponsesTool = getResponsesTool
|
||||
export const googleFormsGetFormTool = getFormTool
|
||||
export const googleFormsCreateFormTool = createFormTool
|
||||
export const googleFormsBatchUpdateTool = batchUpdateTool
|
||||
export const googleFormsSetPublishSettingsTool = setPublishSettingsTool
|
||||
export const googleFormsCreateWatchTool = createWatchTool
|
||||
export const googleFormsListWatchesTool = listWatchesTool
|
||||
export const googleFormsDeleteWatchTool = deleteWatchTool
|
||||
export const googleFormsRenewWatchTool = renewWatchTool
|
||||
|
||||
export * from './types'
|
||||
99
apps/sim/tools/google_forms/list_watches.ts
Normal file
99
apps/sim/tools/google_forms/list_watches.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import type {
|
||||
GoogleFormsListWatchesParams,
|
||||
GoogleFormsListWatchesResponse,
|
||||
GoogleFormsWatch,
|
||||
} from '@/tools/google_forms/types'
|
||||
import { buildListWatchesUrl } from '@/tools/google_forms/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface ListWatchesApiResponse {
|
||||
watches?: GoogleFormsWatch[]
|
||||
}
|
||||
|
||||
export const listWatchesTool: ToolConfig<
|
||||
GoogleFormsListWatchesParams,
|
||||
GoogleFormsListWatchesResponse
|
||||
> = {
|
||||
id: 'google_forms_list_watches',
|
||||
name: 'Google Forms: List Watches',
|
||||
description: 'List all notification watches for a form',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-forms',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
formId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'The ID of the Google Form',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: GoogleFormsListWatchesParams) => buildListWatchesUrl(params.formId),
|
||||
method: 'GET',
|
||||
headers: (params: GoogleFormsListWatchesParams) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = (await response.json()) as ListWatchesApiResponse
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = data as unknown as { error?: { message?: string } }
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
watches: [],
|
||||
},
|
||||
error: errorData.error?.message ?? 'Failed to list watches',
|
||||
}
|
||||
}
|
||||
|
||||
const watches = (data.watches ?? []).map((watch) => ({
|
||||
id: watch.id,
|
||||
target: watch.target,
|
||||
eventType: watch.eventType,
|
||||
createTime: watch.createTime,
|
||||
expireTime: watch.expireTime,
|
||||
state: watch.state,
|
||||
errorType: watch.errorType,
|
||||
}))
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
watches,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
watches: {
|
||||
type: 'array',
|
||||
description: 'List of watches for the form',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Watch ID' },
|
||||
eventType: { type: 'string', description: 'Event type (SCHEMA or RESPONSES)' },
|
||||
createTime: { type: 'string', description: 'When the watch was created' },
|
||||
expireTime: { type: 'string', description: 'When the watch expires' },
|
||||
state: { type: 'string', description: 'Watch state' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
87
apps/sim/tools/google_forms/renew_watch.ts
Normal file
87
apps/sim/tools/google_forms/renew_watch.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import type {
|
||||
GoogleFormsRenewWatchParams,
|
||||
GoogleFormsRenewWatchResponse,
|
||||
GoogleFormsWatch,
|
||||
} from '@/tools/google_forms/types'
|
||||
import { buildRenewWatchUrl } from '@/tools/google_forms/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const renewWatchTool: ToolConfig<
|
||||
GoogleFormsRenewWatchParams,
|
||||
GoogleFormsRenewWatchResponse
|
||||
> = {
|
||||
id: 'google_forms_renew_watch',
|
||||
name: 'Google Forms: Renew Watch',
|
||||
description: 'Renew a notification watch for another 7 days',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-forms',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
formId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'The ID of the Google Form',
|
||||
},
|
||||
watchId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'The ID of the watch to renew',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: GoogleFormsRenewWatchParams) => buildRenewWatchUrl(params.formId, params.watchId),
|
||||
method: 'POST',
|
||||
headers: (params: GoogleFormsRenewWatchParams) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = (await response.json()) as GoogleFormsWatch
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = data as unknown as { error?: { message?: string } }
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
id: '',
|
||||
eventType: null,
|
||||
expireTime: null,
|
||||
state: null,
|
||||
},
|
||||
error: errorData.error?.message ?? 'Failed to renew watch',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? '',
|
||||
eventType: data.eventType ?? null,
|
||||
expireTime: data.expireTime ?? null,
|
||||
state: data.state ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'The watch ID' },
|
||||
eventType: { type: 'string', description: 'The event type being watched', optional: true },
|
||||
expireTime: { type: 'string', description: 'The new expiration time', optional: true },
|
||||
state: { type: 'string', description: 'The watch state', optional: true },
|
||||
},
|
||||
}
|
||||
119
apps/sim/tools/google_forms/set_publish_settings.ts
Normal file
119
apps/sim/tools/google_forms/set_publish_settings.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import type {
|
||||
GoogleFormsPublishSettings,
|
||||
GoogleFormsSetPublishSettingsParams,
|
||||
GoogleFormsSetPublishSettingsResponse,
|
||||
} from '@/tools/google_forms/types'
|
||||
import { buildSetPublishSettingsUrl } from '@/tools/google_forms/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface SetPublishSettingsApiResponse {
|
||||
formId?: string
|
||||
publishSettings?: GoogleFormsPublishSettings
|
||||
}
|
||||
|
||||
export const setPublishSettingsTool: ToolConfig<
|
||||
GoogleFormsSetPublishSettingsParams,
|
||||
GoogleFormsSetPublishSettingsResponse
|
||||
> = {
|
||||
id: 'google_forms_set_publish_settings',
|
||||
name: 'Google Forms: Set Publish Settings',
|
||||
description: 'Update the publish settings of a form (publish/unpublish, accept responses)',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-forms',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
formId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'The ID of the Google Form',
|
||||
},
|
||||
isPublished: {
|
||||
type: 'boolean',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether the form is published and visible to others',
|
||||
},
|
||||
isAcceptingResponses: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether the form accepts responses (forced to false if isPublished is false)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: GoogleFormsSetPublishSettingsParams) => buildSetPublishSettingsUrl(params.formId),
|
||||
method: 'POST',
|
||||
headers: (params: GoogleFormsSetPublishSettingsParams) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params: GoogleFormsSetPublishSettingsParams) => ({
|
||||
publishSettings: {
|
||||
publishState: {
|
||||
isPublished: params.isPublished,
|
||||
...(params.isAcceptingResponses !== undefined
|
||||
? { isAcceptingResponses: params.isAcceptingResponses }
|
||||
: {}),
|
||||
},
|
||||
},
|
||||
updateMask: 'publishState',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = (await response.json()) as SetPublishSettingsApiResponse
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = data as unknown as { error?: { message?: string } }
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
formId: '',
|
||||
publishSettings: {},
|
||||
},
|
||||
error: errorData.error?.message ?? 'Failed to set publish settings',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
formId: data.formId ?? '',
|
||||
publishSettings: data.publishSettings ?? {},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
formId: { type: 'string', description: 'The form ID' },
|
||||
publishSettings: {
|
||||
type: 'json',
|
||||
description: 'The updated publish settings',
|
||||
properties: {
|
||||
publishState: {
|
||||
type: 'object',
|
||||
description: 'The publish state',
|
||||
properties: {
|
||||
isPublished: { type: 'boolean', description: 'Whether the form is published' },
|
||||
isAcceptingResponses: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the form accepts responses',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
268
apps/sim/tools/google_forms/types.ts
Normal file
268
apps/sim/tools/google_forms/types.ts
Normal file
@@ -0,0 +1,268 @@
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
// ============================================
|
||||
// Common Types
|
||||
// ============================================
|
||||
|
||||
export interface GoogleFormsResponse {
|
||||
responseId?: string
|
||||
createTime?: string
|
||||
lastSubmittedTime?: string
|
||||
answers?: Record<string, unknown>
|
||||
respondentEmail?: string
|
||||
totalScore?: number
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
export interface GoogleFormsResponseList {
|
||||
responses?: GoogleFormsResponse[]
|
||||
nextPageToken?: string
|
||||
}
|
||||
|
||||
export interface GoogleFormsInfo {
|
||||
title?: string
|
||||
description?: string
|
||||
documentTitle?: string
|
||||
}
|
||||
|
||||
export interface GoogleFormsSettings {
|
||||
quizSettings?: {
|
||||
isQuiz?: boolean
|
||||
}
|
||||
emailCollectionType?:
|
||||
| 'EMAIL_COLLECTION_TYPE_UNSPECIFIED'
|
||||
| 'DO_NOT_COLLECT'
|
||||
| 'VERIFIED'
|
||||
| 'RESPONDER_INPUT'
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
export interface GoogleFormsPublishState {
|
||||
isPublished?: boolean
|
||||
isAcceptingResponses?: boolean
|
||||
}
|
||||
|
||||
export interface GoogleFormsPublishSettings {
|
||||
publishState?: GoogleFormsPublishState
|
||||
}
|
||||
|
||||
export interface GoogleFormsItem {
|
||||
itemId?: string
|
||||
title?: string
|
||||
description?: string
|
||||
questionItem?: Record<string, unknown>
|
||||
questionGroupItem?: Record<string, unknown>
|
||||
pageBreakItem?: Record<string, unknown>
|
||||
textItem?: Record<string, unknown>
|
||||
imageItem?: Record<string, unknown>
|
||||
videoItem?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface GoogleForm {
|
||||
formId?: string
|
||||
info?: GoogleFormsInfo
|
||||
settings?: GoogleFormsSettings
|
||||
items?: GoogleFormsItem[]
|
||||
revisionId?: string
|
||||
responderUri?: string
|
||||
linkedSheetId?: string
|
||||
publishSettings?: GoogleFormsPublishSettings
|
||||
}
|
||||
|
||||
export interface GoogleFormsWatch {
|
||||
id?: string
|
||||
target?: {
|
||||
topic?: {
|
||||
topicName?: string
|
||||
}
|
||||
}
|
||||
eventType?: 'EVENT_TYPE_UNSPECIFIED' | 'SCHEMA' | 'RESPONSES'
|
||||
createTime?: string
|
||||
expireTime?: string
|
||||
state?: 'STATE_UNSPECIFIED' | 'ACTIVE' | 'SUSPENDED'
|
||||
errorType?: string
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Get Responses Params
|
||||
// ============================================
|
||||
|
||||
export interface GoogleFormsGetResponsesParams {
|
||||
accessToken: string
|
||||
formId: string
|
||||
responseId?: string
|
||||
pageSize?: number
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Get Form Params & Response
|
||||
// ============================================
|
||||
|
||||
export interface GoogleFormsGetFormParams {
|
||||
accessToken: string
|
||||
formId: string
|
||||
}
|
||||
|
||||
export interface GoogleFormsGetFormResponse extends ToolResponse {
|
||||
output: {
|
||||
formId: string
|
||||
title: string | null
|
||||
description: string | null
|
||||
documentTitle: string | null
|
||||
responderUri: string | null
|
||||
linkedSheetId: string | null
|
||||
revisionId: string | null
|
||||
items: GoogleFormsItem[]
|
||||
settings: GoogleFormsSettings | null
|
||||
publishSettings: GoogleFormsPublishSettings | null
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Create Form Params & Response
|
||||
// ============================================
|
||||
|
||||
export interface GoogleFormsCreateFormParams {
|
||||
accessToken: string
|
||||
title: string
|
||||
documentTitle?: string
|
||||
unpublished?: boolean
|
||||
}
|
||||
|
||||
export interface GoogleFormsCreateFormResponse extends ToolResponse {
|
||||
output: {
|
||||
formId: string
|
||||
title: string | null
|
||||
documentTitle: string | null
|
||||
responderUri: string | null
|
||||
revisionId: string | null
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Batch Update Params & Response
|
||||
// ============================================
|
||||
|
||||
export interface GoogleFormsBatchUpdateRequest {
|
||||
updateFormInfo?: {
|
||||
info: Partial<GoogleFormsInfo>
|
||||
updateMask: string
|
||||
}
|
||||
updateSettings?: {
|
||||
settings: Partial<GoogleFormsSettings>
|
||||
updateMask: string
|
||||
}
|
||||
createItem?: {
|
||||
item: GoogleFormsItem
|
||||
location: { index: number }
|
||||
}
|
||||
updateItem?: {
|
||||
item: GoogleFormsItem
|
||||
location: { index: number }
|
||||
updateMask: string
|
||||
}
|
||||
moveItem?: {
|
||||
originalLocation: { index: number }
|
||||
newLocation: { index: number }
|
||||
}
|
||||
deleteItem?: {
|
||||
location: { index: number }
|
||||
}
|
||||
}
|
||||
|
||||
export interface GoogleFormsBatchUpdateParams {
|
||||
accessToken: string
|
||||
formId: string
|
||||
requests: GoogleFormsBatchUpdateRequest[]
|
||||
includeFormInResponse?: boolean
|
||||
}
|
||||
|
||||
export interface GoogleFormsBatchUpdateResponse extends ToolResponse {
|
||||
output: {
|
||||
replies: Record<string, unknown>[]
|
||||
writeControl: {
|
||||
requiredRevisionId?: string
|
||||
targetRevisionId?: string
|
||||
} | null
|
||||
form: GoogleForm | null
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Set Publish Settings Params & Response
|
||||
// ============================================
|
||||
|
||||
export interface GoogleFormsSetPublishSettingsParams {
|
||||
accessToken: string
|
||||
formId: string
|
||||
isPublished: boolean
|
||||
isAcceptingResponses?: boolean
|
||||
}
|
||||
|
||||
export interface GoogleFormsSetPublishSettingsResponse extends ToolResponse {
|
||||
output: {
|
||||
formId: string
|
||||
publishSettings: GoogleFormsPublishSettings
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Watch Params & Responses
|
||||
// ============================================
|
||||
|
||||
export interface GoogleFormsCreateWatchParams {
|
||||
accessToken: string
|
||||
formId: string
|
||||
eventType: 'SCHEMA' | 'RESPONSES'
|
||||
topicName: string
|
||||
watchId?: string
|
||||
}
|
||||
|
||||
export interface GoogleFormsCreateWatchResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
eventType: string
|
||||
topicName: string | null
|
||||
createTime: string | null
|
||||
expireTime: string | null
|
||||
state: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface GoogleFormsListWatchesParams {
|
||||
accessToken: string
|
||||
formId: string
|
||||
}
|
||||
|
||||
export interface GoogleFormsListWatchesResponse extends ToolResponse {
|
||||
output: {
|
||||
watches: GoogleFormsWatch[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface GoogleFormsDeleteWatchParams {
|
||||
accessToken: string
|
||||
formId: string
|
||||
watchId: string
|
||||
}
|
||||
|
||||
export interface GoogleFormsDeleteWatchResponse extends ToolResponse {
|
||||
output: {
|
||||
deleted: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface GoogleFormsRenewWatchParams {
|
||||
accessToken: string
|
||||
formId: string
|
||||
watchId: string
|
||||
}
|
||||
|
||||
export interface GoogleFormsRenewWatchResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
eventType: string | null
|
||||
expireTime: string | null
|
||||
state: string | null
|
||||
}
|
||||
}
|
||||
76
apps/sim/tools/google_forms/utils.ts
Normal file
76
apps/sim/tools/google_forms/utils.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
|
||||
export const FORMS_API_BASE = 'https://forms.googleapis.com/v1'
|
||||
|
||||
const logger = createLogger('GoogleFormsUtils')
|
||||
|
||||
export function buildListResponsesUrl(params: { formId: string; pageSize?: number }): string {
|
||||
const { formId, pageSize } = params
|
||||
const url = new URL(`${FORMS_API_BASE}/forms/${encodeURIComponent(formId)}/responses`)
|
||||
if (pageSize && pageSize > 0) {
|
||||
const limited = Math.min(pageSize, 5000)
|
||||
url.searchParams.set('pageSize', String(limited))
|
||||
}
|
||||
const finalUrl = url.toString()
|
||||
logger.debug('Built Google Forms list responses URL', { finalUrl })
|
||||
return finalUrl
|
||||
}
|
||||
|
||||
export function buildGetResponseUrl(params: { formId: string; responseId: string }): string {
|
||||
const { formId, responseId } = params
|
||||
const finalUrl = `${FORMS_API_BASE}/forms/${encodeURIComponent(formId)}/responses/${encodeURIComponent(responseId)}`
|
||||
logger.debug('Built Google Forms get response URL', { finalUrl })
|
||||
return finalUrl
|
||||
}
|
||||
|
||||
export function buildGetFormUrl(formId: string): string {
|
||||
const finalUrl = `${FORMS_API_BASE}/forms/${encodeURIComponent(formId)}`
|
||||
logger.debug('Built Google Forms get form URL', { finalUrl })
|
||||
return finalUrl
|
||||
}
|
||||
|
||||
export function buildCreateFormUrl(unpublished?: boolean): string {
|
||||
const url = new URL(`${FORMS_API_BASE}/forms`)
|
||||
if (unpublished) {
|
||||
url.searchParams.set('unpublished', 'true')
|
||||
}
|
||||
const finalUrl = url.toString()
|
||||
logger.debug('Built Google Forms create form URL', { finalUrl })
|
||||
return finalUrl
|
||||
}
|
||||
|
||||
export function buildBatchUpdateUrl(formId: string): string {
|
||||
const finalUrl = `${FORMS_API_BASE}/forms/${encodeURIComponent(formId)}:batchUpdate`
|
||||
logger.debug('Built Google Forms batch update URL', { finalUrl })
|
||||
return finalUrl
|
||||
}
|
||||
|
||||
export function buildSetPublishSettingsUrl(formId: string): string {
|
||||
const finalUrl = `${FORMS_API_BASE}/forms/${encodeURIComponent(formId)}:setPublishSettings`
|
||||
logger.debug('Built Google Forms set publish settings URL', { finalUrl })
|
||||
return finalUrl
|
||||
}
|
||||
|
||||
export function buildListWatchesUrl(formId: string): string {
|
||||
const finalUrl = `${FORMS_API_BASE}/forms/${encodeURIComponent(formId)}/watches`
|
||||
logger.debug('Built Google Forms list watches URL', { finalUrl })
|
||||
return finalUrl
|
||||
}
|
||||
|
||||
export function buildCreateWatchUrl(formId: string): string {
|
||||
const finalUrl = `${FORMS_API_BASE}/forms/${encodeURIComponent(formId)}/watches`
|
||||
logger.debug('Built Google Forms create watch URL', { finalUrl })
|
||||
return finalUrl
|
||||
}
|
||||
|
||||
export function buildDeleteWatchUrl(formId: string, watchId: string): string {
|
||||
const finalUrl = `${FORMS_API_BASE}/forms/${encodeURIComponent(formId)}/watches/${encodeURIComponent(watchId)}`
|
||||
logger.debug('Built Google Forms delete watch URL', { finalUrl })
|
||||
return finalUrl
|
||||
}
|
||||
|
||||
export function buildRenewWatchUrl(formId: string, watchId: string): string {
|
||||
const finalUrl = `${FORMS_API_BASE}/forms/${encodeURIComponent(formId)}/watches/${encodeURIComponent(watchId)}:renew`
|
||||
logger.debug('Built Google Forms renew watch URL', { finalUrl })
|
||||
return finalUrl
|
||||
}
|
||||
75
apps/sim/tools/google_groups/add_alias.ts
Normal file
75
apps/sim/tools/google_groups/add_alias.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { GoogleGroupsAddAliasParams, GoogleGroupsAddAliasResponse } from './types'
|
||||
|
||||
export const addAliasTool: ToolConfig<GoogleGroupsAddAliasParams, GoogleGroupsAddAliasResponse> = {
|
||||
id: 'google_groups_add_alias',
|
||||
name: 'Google Groups Add Alias',
|
||||
description: 'Add an email alias 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',
|
||||
},
|
||||
alias: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The email alias to add to the group',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const encodedGroupKey = encodeURIComponent(params.groupKey.trim())
|
||||
return `https://admin.googleapis.com/admin/directory/v1/groups/${encodedGroupKey}/aliases`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => ({
|
||||
alias: params.alias.trim(),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error?.message || 'Failed to add group alias')
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? null,
|
||||
primaryEmail: data.primaryEmail ?? null,
|
||||
alias: data.alias ?? null,
|
||||
kind: data.kind ?? null,
|
||||
etag: data.etag ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Unique group identifier' },
|
||||
primaryEmail: { type: 'string', description: "Group's primary email address" },
|
||||
alias: { type: 'string', description: 'The alias that was added' },
|
||||
kind: { type: 'string', description: 'API resource type' },
|
||||
etag: { type: 'string', description: 'Resource version identifier' },
|
||||
},
|
||||
}
|
||||
151
apps/sim/tools/google_groups/get_settings.ts
Normal file
151
apps/sim/tools/google_groups/get_settings.ts
Normal file
@@ -0,0 +1,151 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { GoogleGroupsGetSettingsParams, GoogleGroupsGetSettingsResponse } from './types'
|
||||
|
||||
export const getSettingsTool: ToolConfig<
|
||||
GoogleGroupsGetSettingsParams,
|
||||
GoogleGroupsGetSettingsResponse
|
||||
> = {
|
||||
id: 'google_groups_get_settings',
|
||||
name: 'Google Groups Get Settings',
|
||||
description:
|
||||
'Get the settings for a Google Group including access permissions, moderation, and posting options',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-groups',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
groupEmail: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The email address of the group',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const encodedEmail = encodeURIComponent(params.groupEmail.trim())
|
||||
return `https://www.googleapis.com/groups/v1/groups/${encodedEmail}`
|
||||
},
|
||||
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 settings')
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
email: data.email ?? null,
|
||||
name: data.name ?? null,
|
||||
description: data.description ?? null,
|
||||
whoCanJoin: data.whoCanJoin ?? null,
|
||||
whoCanViewMembership: data.whoCanViewMembership ?? null,
|
||||
whoCanViewGroup: data.whoCanViewGroup ?? null,
|
||||
whoCanPostMessage: data.whoCanPostMessage ?? null,
|
||||
allowExternalMembers: data.allowExternalMembers ?? null,
|
||||
allowWebPosting: data.allowWebPosting ?? null,
|
||||
primaryLanguage: data.primaryLanguage ?? null,
|
||||
isArchived: data.isArchived ?? null,
|
||||
archiveOnly: data.archiveOnly ?? null,
|
||||
messageModerationLevel: data.messageModerationLevel ?? null,
|
||||
spamModerationLevel: data.spamModerationLevel ?? null,
|
||||
replyTo: data.replyTo ?? null,
|
||||
customReplyTo: data.customReplyTo ?? null,
|
||||
includeCustomFooter: data.includeCustomFooter ?? null,
|
||||
customFooterText: data.customFooterText ?? null,
|
||||
sendMessageDenyNotification: data.sendMessageDenyNotification ?? null,
|
||||
defaultMessageDenyNotificationText: data.defaultMessageDenyNotificationText ?? null,
|
||||
membersCanPostAsTheGroup: data.membersCanPostAsTheGroup ?? null,
|
||||
includeInGlobalAddressList: data.includeInGlobalAddressList ?? null,
|
||||
whoCanLeaveGroup: data.whoCanLeaveGroup ?? null,
|
||||
whoCanContactOwner: data.whoCanContactOwner ?? null,
|
||||
favoriteRepliesOnTop: data.favoriteRepliesOnTop ?? null,
|
||||
whoCanApproveMembers: data.whoCanApproveMembers ?? null,
|
||||
whoCanBanUsers: data.whoCanBanUsers ?? null,
|
||||
whoCanModerateMembers: data.whoCanModerateMembers ?? null,
|
||||
whoCanModerateContent: data.whoCanModerateContent ?? null,
|
||||
whoCanAssistContent: data.whoCanAssistContent ?? null,
|
||||
enableCollaborativeInbox: data.enableCollaborativeInbox ?? null,
|
||||
whoCanDiscoverGroup: data.whoCanDiscoverGroup ?? null,
|
||||
defaultSender: data.defaultSender ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
email: { type: 'string', description: "The group's email address" },
|
||||
name: { type: 'string', description: 'The group name (max 75 characters)' },
|
||||
description: { type: 'string', description: 'The group description (max 4096 characters)' },
|
||||
whoCanJoin: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Who can join the group (ANYONE_CAN_JOIN, ALL_IN_DOMAIN_CAN_JOIN, INVITED_CAN_JOIN, CAN_REQUEST_TO_JOIN)',
|
||||
},
|
||||
whoCanViewMembership: { type: 'string', description: 'Who can view group membership' },
|
||||
whoCanViewGroup: { type: 'string', description: 'Who can view group messages' },
|
||||
whoCanPostMessage: { type: 'string', description: 'Who can post messages to the group' },
|
||||
allowExternalMembers: { type: 'string', description: 'Whether external users can be members' },
|
||||
allowWebPosting: { type: 'string', description: 'Whether web posting is allowed' },
|
||||
primaryLanguage: { type: 'string', description: "The group's primary language" },
|
||||
isArchived: { type: 'string', description: 'Whether messages are archived' },
|
||||
archiveOnly: { type: 'string', description: 'Whether the group is archive-only (inactive)' },
|
||||
messageModerationLevel: { type: 'string', description: 'Message moderation level' },
|
||||
spamModerationLevel: {
|
||||
type: 'string',
|
||||
description: 'Spam handling level (ALLOW, MODERATE, SILENTLY_MODERATE, REJECT)',
|
||||
},
|
||||
replyTo: { type: 'string', description: 'Default reply destination' },
|
||||
customReplyTo: { type: 'string', description: 'Custom email for replies' },
|
||||
includeCustomFooter: { type: 'string', description: 'Whether to include custom footer' },
|
||||
customFooterText: { type: 'string', description: 'Custom footer text (max 1000 characters)' },
|
||||
sendMessageDenyNotification: {
|
||||
type: 'string',
|
||||
description: 'Whether to send rejection notifications',
|
||||
},
|
||||
defaultMessageDenyNotificationText: {
|
||||
type: 'string',
|
||||
description: 'Default rejection message text',
|
||||
},
|
||||
membersCanPostAsTheGroup: {
|
||||
type: 'string',
|
||||
description: 'Whether members can post as the group',
|
||||
},
|
||||
includeInGlobalAddressList: {
|
||||
type: 'string',
|
||||
description: 'Whether included in Global Address List',
|
||||
},
|
||||
whoCanLeaveGroup: { type: 'string', description: 'Who can leave the group' },
|
||||
whoCanContactOwner: { type: 'string', description: 'Who can contact the group owner' },
|
||||
favoriteRepliesOnTop: { type: 'string', description: 'Whether favorite replies appear at top' },
|
||||
whoCanApproveMembers: { type: 'string', description: 'Who can approve new members' },
|
||||
whoCanBanUsers: { type: 'string', description: 'Who can ban users' },
|
||||
whoCanModerateMembers: { type: 'string', description: 'Who can manage members' },
|
||||
whoCanModerateContent: { type: 'string', description: 'Who can moderate content' },
|
||||
whoCanAssistContent: { type: 'string', description: 'Who can assist with content metadata' },
|
||||
enableCollaborativeInbox: {
|
||||
type: 'string',
|
||||
description: 'Whether collaborative inbox is enabled',
|
||||
},
|
||||
whoCanDiscoverGroup: { type: 'string', description: 'Who can discover the group' },
|
||||
defaultSender: {
|
||||
type: 'string',
|
||||
description: 'Default sender identity (DEFAULT_SELF or GROUP)',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,23 +1,33 @@
|
||||
import { addAliasTool } from './add_alias'
|
||||
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 { getSettingsTool } from './get_settings'
|
||||
import { hasMemberTool } from './has_member'
|
||||
import { listAliasesTool } from './list_aliases'
|
||||
import { listGroupsTool } from './list_groups'
|
||||
import { listMembersTool } from './list_members'
|
||||
import { removeAliasTool } from './remove_alias'
|
||||
import { removeMemberTool } from './remove_member'
|
||||
import { updateGroupTool } from './update_group'
|
||||
import { updateMemberTool } from './update_member'
|
||||
import { updateSettingsTool } from './update_settings'
|
||||
|
||||
export const googleGroupsAddAliasTool = addAliasTool
|
||||
export const googleGroupsAddMemberTool = addMemberTool
|
||||
export const googleGroupsCreateGroupTool = createGroupTool
|
||||
export const googleGroupsDeleteGroupTool = deleteGroupTool
|
||||
export const googleGroupsGetGroupTool = getGroupTool
|
||||
export const googleGroupsGetMemberTool = getMemberTool
|
||||
export const googleGroupsGetSettingsTool = getSettingsTool
|
||||
export const googleGroupsHasMemberTool = hasMemberTool
|
||||
export const googleGroupsListAliasesTool = listAliasesTool
|
||||
export const googleGroupsListGroupsTool = listGroupsTool
|
||||
export const googleGroupsListMembersTool = listMembersTool
|
||||
export const googleGroupsRemoveAliasTool = removeAliasTool
|
||||
export const googleGroupsRemoveMemberTool = removeMemberTool
|
||||
export const googleGroupsUpdateGroupTool = updateGroupTool
|
||||
export const googleGroupsUpdateMemberTool = updateMemberTool
|
||||
export const googleGroupsUpdateSettingsTool = updateSettingsTool
|
||||
|
||||
74
apps/sim/tools/google_groups/list_aliases.ts
Normal file
74
apps/sim/tools/google_groups/list_aliases.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { GoogleGroupsListAliasesParams, GoogleGroupsListAliasesResponse } from './types'
|
||||
|
||||
export const listAliasesTool: ToolConfig<
|
||||
GoogleGroupsListAliasesParams,
|
||||
GoogleGroupsListAliasesResponse
|
||||
> = {
|
||||
id: 'google_groups_list_aliases',
|
||||
name: 'Google Groups List Aliases',
|
||||
description: 'List all email aliases for 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',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const encodedGroupKey = encodeURIComponent(params.groupKey.trim())
|
||||
return `https://admin.googleapis.com/admin/directory/v1/groups/${encodedGroupKey}/aliases`
|
||||
},
|
||||
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 aliases')
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
aliases: data.aliases ?? [],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
aliases: {
|
||||
type: 'array',
|
||||
description: 'List of email aliases for the group',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique group identifier' },
|
||||
primaryEmail: { type: 'string', description: "Group's primary email address" },
|
||||
alias: { type: 'string', description: 'Alias email address' },
|
||||
kind: { type: 'string', description: 'API resource type' },
|
||||
etag: { type: 'string', description: 'Resource version identifier' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
68
apps/sim/tools/google_groups/remove_alias.ts
Normal file
68
apps/sim/tools/google_groups/remove_alias.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { GoogleGroupsRemoveAliasParams, GoogleGroupsRemoveAliasResponse } from './types'
|
||||
|
||||
export const removeAliasTool: ToolConfig<
|
||||
GoogleGroupsRemoveAliasParams,
|
||||
GoogleGroupsRemoveAliasResponse
|
||||
> = {
|
||||
id: 'google_groups_remove_alias',
|
||||
name: 'Google Groups Remove Alias',
|
||||
description: 'Remove an email alias 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',
|
||||
},
|
||||
alias: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The email alias to remove from the group',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const encodedGroupKey = encodeURIComponent(params.groupKey.trim())
|
||||
const encodedAlias = encodeURIComponent(params.alias.trim())
|
||||
return `https://admin.googleapis.com/admin/directory/v1/groups/${encodedGroupKey}/aliases/${encodedAlias}`
|
||||
},
|
||||
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 group alias')
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
deleted: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
deleted: { type: 'boolean', description: 'Whether the alias was successfully deleted' },
|
||||
},
|
||||
}
|
||||
@@ -103,9 +103,197 @@ export interface GoogleGroupsHasMemberParams extends GoogleGroupsCommonParams {
|
||||
memberKey: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters for listing group aliases
|
||||
*/
|
||||
export interface GoogleGroupsListAliasesParams extends GoogleGroupsCommonParams {
|
||||
groupKey: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters for adding a group alias
|
||||
*/
|
||||
export interface GoogleGroupsAddAliasParams extends GoogleGroupsCommonParams {
|
||||
groupKey: string
|
||||
alias: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters for removing a group alias
|
||||
*/
|
||||
export interface GoogleGroupsRemoveAliasParams extends GoogleGroupsCommonParams {
|
||||
groupKey: string
|
||||
alias: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters for getting group settings
|
||||
*/
|
||||
export interface GoogleGroupsGetSettingsParams extends GoogleGroupsCommonParams {
|
||||
groupEmail: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters for updating group settings
|
||||
*/
|
||||
export interface GoogleGroupsUpdateSettingsParams extends GoogleGroupsCommonParams {
|
||||
groupEmail: string
|
||||
name?: string
|
||||
description?: string
|
||||
whoCanJoin?: string
|
||||
whoCanViewMembership?: string
|
||||
whoCanViewGroup?: string
|
||||
whoCanPostMessage?: string
|
||||
allowExternalMembers?: string
|
||||
allowWebPosting?: string
|
||||
primaryLanguage?: string
|
||||
isArchived?: string
|
||||
archiveOnly?: string
|
||||
messageModerationLevel?: string
|
||||
spamModerationLevel?: string
|
||||
replyTo?: string
|
||||
customReplyTo?: string
|
||||
includeCustomFooter?: string
|
||||
customFooterText?: string
|
||||
sendMessageDenyNotification?: string
|
||||
defaultMessageDenyNotificationText?: string
|
||||
membersCanPostAsTheGroup?: string
|
||||
includeInGlobalAddressList?: string
|
||||
whoCanLeaveGroup?: string
|
||||
whoCanContactOwner?: string
|
||||
favoriteRepliesOnTop?: string
|
||||
whoCanApproveMembers?: string
|
||||
whoCanBanUsers?: string
|
||||
whoCanModerateMembers?: string
|
||||
whoCanModerateContent?: string
|
||||
whoCanAssistContent?: string
|
||||
enableCollaborativeInbox?: string
|
||||
whoCanDiscoverGroup?: string
|
||||
defaultSender?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard response for Google Groups operations
|
||||
*/
|
||||
export interface GoogleGroupsResponse extends ToolResponse {
|
||||
output: Record<string, unknown>
|
||||
}
|
||||
|
||||
/**
|
||||
* Response for listing group aliases
|
||||
*/
|
||||
export interface GoogleGroupsListAliasesResponse extends ToolResponse {
|
||||
output: {
|
||||
aliases: Array<{
|
||||
id?: string
|
||||
primaryEmail?: string
|
||||
alias?: string
|
||||
kind?: string
|
||||
etag?: string
|
||||
}>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Response for adding a group alias
|
||||
*/
|
||||
export interface GoogleGroupsAddAliasResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string | null
|
||||
primaryEmail: string | null
|
||||
alias: string | null
|
||||
kind: string | null
|
||||
etag: string | null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Response for removing a group alias
|
||||
*/
|
||||
export interface GoogleGroupsRemoveAliasResponse extends ToolResponse {
|
||||
output: {
|
||||
deleted: boolean
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Response for getting group settings
|
||||
*/
|
||||
export interface GoogleGroupsGetSettingsResponse extends ToolResponse {
|
||||
output: {
|
||||
email: string | null
|
||||
name: string | null
|
||||
description: string | null
|
||||
whoCanJoin: string | null
|
||||
whoCanViewMembership: string | null
|
||||
whoCanViewGroup: string | null
|
||||
whoCanPostMessage: string | null
|
||||
allowExternalMembers: string | null
|
||||
allowWebPosting: string | null
|
||||
primaryLanguage: string | null
|
||||
isArchived: string | null
|
||||
archiveOnly: string | null
|
||||
messageModerationLevel: string | null
|
||||
spamModerationLevel: string | null
|
||||
replyTo: string | null
|
||||
customReplyTo: string | null
|
||||
includeCustomFooter: string | null
|
||||
customFooterText: string | null
|
||||
sendMessageDenyNotification: string | null
|
||||
defaultMessageDenyNotificationText: string | null
|
||||
membersCanPostAsTheGroup: string | null
|
||||
includeInGlobalAddressList: string | null
|
||||
whoCanLeaveGroup: string | null
|
||||
whoCanContactOwner: string | null
|
||||
favoriteRepliesOnTop: string | null
|
||||
whoCanApproveMembers: string | null
|
||||
whoCanBanUsers: string | null
|
||||
whoCanModerateMembers: string | null
|
||||
whoCanModerateContent: string | null
|
||||
whoCanAssistContent: string | null
|
||||
enableCollaborativeInbox: string | null
|
||||
whoCanDiscoverGroup: string | null
|
||||
defaultSender: string | null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Response for updating group settings
|
||||
*/
|
||||
export interface GoogleGroupsUpdateSettingsResponse extends ToolResponse {
|
||||
output: {
|
||||
email: string | null
|
||||
name: string | null
|
||||
description: string | null
|
||||
whoCanJoin: string | null
|
||||
whoCanViewMembership: string | null
|
||||
whoCanViewGroup: string | null
|
||||
whoCanPostMessage: string | null
|
||||
allowExternalMembers: string | null
|
||||
allowWebPosting: string | null
|
||||
primaryLanguage: string | null
|
||||
isArchived: string | null
|
||||
archiveOnly: string | null
|
||||
messageModerationLevel: string | null
|
||||
spamModerationLevel: string | null
|
||||
replyTo: string | null
|
||||
customReplyTo: string | null
|
||||
includeCustomFooter: string | null
|
||||
customFooterText: string | null
|
||||
sendMessageDenyNotification: string | null
|
||||
defaultMessageDenyNotificationText: string | null
|
||||
membersCanPostAsTheGroup: string | null
|
||||
includeInGlobalAddressList: string | null
|
||||
whoCanLeaveGroup: string | null
|
||||
whoCanContactOwner: string | null
|
||||
favoriteRepliesOnTop: string | null
|
||||
whoCanApproveMembers: string | null
|
||||
whoCanBanUsers: string | null
|
||||
whoCanModerateMembers: string | null
|
||||
whoCanModerateContent: string | null
|
||||
whoCanAssistContent: string | null
|
||||
enableCollaborativeInbox: string | null
|
||||
whoCanDiscoverGroup: string | null
|
||||
defaultSender: string | null
|
||||
}
|
||||
}
|
||||
|
||||
396
apps/sim/tools/google_groups/update_settings.ts
Normal file
396
apps/sim/tools/google_groups/update_settings.ts
Normal file
@@ -0,0 +1,396 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { GoogleGroupsUpdateSettingsParams, GoogleGroupsUpdateSettingsResponse } from './types'
|
||||
|
||||
export const updateSettingsTool: ToolConfig<
|
||||
GoogleGroupsUpdateSettingsParams,
|
||||
GoogleGroupsUpdateSettingsResponse
|
||||
> = {
|
||||
id: 'google_groups_update_settings',
|
||||
name: 'Google Groups Update Settings',
|
||||
description:
|
||||
'Update the settings for a Google Group including access permissions, moderation, and posting options',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-groups',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
groupEmail: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The email address of the group',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The group name (max 75 characters)',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The group description (max 4096 characters)',
|
||||
},
|
||||
whoCanJoin: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Who can join: ANYONE_CAN_JOIN, ALL_IN_DOMAIN_CAN_JOIN, INVITED_CAN_JOIN, CAN_REQUEST_TO_JOIN',
|
||||
},
|
||||
whoCanViewMembership: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Who can view membership: ALL_IN_DOMAIN_CAN_VIEW, ALL_MEMBERS_CAN_VIEW, ALL_MANAGERS_CAN_VIEW',
|
||||
},
|
||||
whoCanViewGroup: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Who can view group messages: ANYONE_CAN_VIEW, ALL_IN_DOMAIN_CAN_VIEW, ALL_MEMBERS_CAN_VIEW, ALL_MANAGERS_CAN_VIEW',
|
||||
},
|
||||
whoCanPostMessage: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Who can post: NONE_CAN_POST, ALL_MANAGERS_CAN_POST, ALL_MEMBERS_CAN_POST, ALL_OWNERS_CAN_POST, ALL_IN_DOMAIN_CAN_POST, ANYONE_CAN_POST',
|
||||
},
|
||||
allowExternalMembers: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether external users can be members: true or false',
|
||||
},
|
||||
allowWebPosting: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether web posting is allowed: true or false',
|
||||
},
|
||||
primaryLanguage: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: "The group's primary language (e.g., en)",
|
||||
},
|
||||
isArchived: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether messages are archived: true or false',
|
||||
},
|
||||
archiveOnly: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether the group is archive-only (inactive): true or false',
|
||||
},
|
||||
messageModerationLevel: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Message moderation: MODERATE_ALL_MESSAGES, MODERATE_NON_MEMBERS, MODERATE_NEW_MEMBERS, MODERATE_NONE',
|
||||
},
|
||||
spamModerationLevel: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Spam handling: ALLOW, MODERATE, SILENTLY_MODERATE, REJECT',
|
||||
},
|
||||
replyTo: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Default reply: REPLY_TO_CUSTOM, REPLY_TO_SENDER, REPLY_TO_LIST, REPLY_TO_OWNER, REPLY_TO_IGNORE, REPLY_TO_MANAGERS',
|
||||
},
|
||||
customReplyTo: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Custom email for replies (when replyTo is REPLY_TO_CUSTOM)',
|
||||
},
|
||||
includeCustomFooter: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to include custom footer: true or false',
|
||||
},
|
||||
customFooterText: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Custom footer text (max 1000 characters)',
|
||||
},
|
||||
sendMessageDenyNotification: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to send rejection notifications: true or false',
|
||||
},
|
||||
defaultMessageDenyNotificationText: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Default rejection message text',
|
||||
},
|
||||
membersCanPostAsTheGroup: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether members can post as the group: true or false',
|
||||
},
|
||||
includeInGlobalAddressList: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether included in Global Address List: true or false',
|
||||
},
|
||||
whoCanLeaveGroup: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Who can leave: ALL_MANAGERS_CAN_LEAVE, ALL_MEMBERS_CAN_LEAVE, NONE_CAN_LEAVE',
|
||||
},
|
||||
whoCanContactOwner: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Who can contact owner: ALL_IN_DOMAIN_CAN_CONTACT, ALL_MANAGERS_CAN_CONTACT, ALL_MEMBERS_CAN_CONTACT, ANYONE_CAN_CONTACT',
|
||||
},
|
||||
favoriteRepliesOnTop: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether favorite replies appear at top: true or false',
|
||||
},
|
||||
whoCanApproveMembers: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Who can approve members: ALL_OWNERS_CAN_APPROVE, ALL_MANAGERS_CAN_APPROVE, ALL_MEMBERS_CAN_APPROVE, NONE_CAN_APPROVE',
|
||||
},
|
||||
whoCanBanUsers: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Who can ban users: OWNERS_ONLY, OWNERS_AND_MANAGERS, NONE',
|
||||
},
|
||||
whoCanModerateMembers: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Who can manage members: OWNERS_ONLY, OWNERS_AND_MANAGERS, ALL_MEMBERS, NONE',
|
||||
},
|
||||
whoCanModerateContent: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Who can moderate content: OWNERS_ONLY, OWNERS_AND_MANAGERS, ALL_MEMBERS, NONE',
|
||||
},
|
||||
whoCanAssistContent: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Who can assist with content metadata: OWNERS_ONLY, OWNERS_AND_MANAGERS, ALL_MEMBERS, NONE',
|
||||
},
|
||||
enableCollaborativeInbox: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether collaborative inbox is enabled: true or false',
|
||||
},
|
||||
whoCanDiscoverGroup: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Who can discover: ANYONE_CAN_DISCOVER, ALL_IN_DOMAIN_CAN_DISCOVER, ALL_MEMBERS_CAN_DISCOVER',
|
||||
},
|
||||
defaultSender: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Default sender: DEFAULT_SELF or GROUP',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const encodedEmail = encodeURIComponent(params.groupEmail.trim())
|
||||
return `https://www.googleapis.com/groups/v1/groups/${encodedEmail}`
|
||||
},
|
||||
method: 'PATCH',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, string> = {}
|
||||
if (params.name !== undefined) body.name = params.name
|
||||
if (params.description !== undefined) body.description = params.description
|
||||
if (params.whoCanJoin !== undefined) body.whoCanJoin = params.whoCanJoin
|
||||
if (params.whoCanViewMembership !== undefined)
|
||||
body.whoCanViewMembership = params.whoCanViewMembership
|
||||
if (params.whoCanViewGroup !== undefined) body.whoCanViewGroup = params.whoCanViewGroup
|
||||
if (params.whoCanPostMessage !== undefined) body.whoCanPostMessage = params.whoCanPostMessage
|
||||
if (params.allowExternalMembers !== undefined)
|
||||
body.allowExternalMembers = params.allowExternalMembers
|
||||
if (params.allowWebPosting !== undefined) body.allowWebPosting = params.allowWebPosting
|
||||
if (params.primaryLanguage !== undefined) body.primaryLanguage = params.primaryLanguage
|
||||
if (params.isArchived !== undefined) body.isArchived = params.isArchived
|
||||
if (params.archiveOnly !== undefined) body.archiveOnly = params.archiveOnly
|
||||
if (params.messageModerationLevel !== undefined)
|
||||
body.messageModerationLevel = params.messageModerationLevel
|
||||
if (params.spamModerationLevel !== undefined)
|
||||
body.spamModerationLevel = params.spamModerationLevel
|
||||
if (params.replyTo !== undefined) body.replyTo = params.replyTo
|
||||
if (params.customReplyTo !== undefined) body.customReplyTo = params.customReplyTo
|
||||
if (params.includeCustomFooter !== undefined)
|
||||
body.includeCustomFooter = params.includeCustomFooter
|
||||
if (params.customFooterText !== undefined) body.customFooterText = params.customFooterText
|
||||
if (params.sendMessageDenyNotification !== undefined)
|
||||
body.sendMessageDenyNotification = params.sendMessageDenyNotification
|
||||
if (params.defaultMessageDenyNotificationText !== undefined)
|
||||
body.defaultMessageDenyNotificationText = params.defaultMessageDenyNotificationText
|
||||
if (params.membersCanPostAsTheGroup !== undefined)
|
||||
body.membersCanPostAsTheGroup = params.membersCanPostAsTheGroup
|
||||
if (params.includeInGlobalAddressList !== undefined)
|
||||
body.includeInGlobalAddressList = params.includeInGlobalAddressList
|
||||
if (params.whoCanLeaveGroup !== undefined) body.whoCanLeaveGroup = params.whoCanLeaveGroup
|
||||
if (params.whoCanContactOwner !== undefined)
|
||||
body.whoCanContactOwner = params.whoCanContactOwner
|
||||
if (params.favoriteRepliesOnTop !== undefined)
|
||||
body.favoriteRepliesOnTop = params.favoriteRepliesOnTop
|
||||
if (params.whoCanApproveMembers !== undefined)
|
||||
body.whoCanApproveMembers = params.whoCanApproveMembers
|
||||
if (params.whoCanBanUsers !== undefined) body.whoCanBanUsers = params.whoCanBanUsers
|
||||
if (params.whoCanModerateMembers !== undefined)
|
||||
body.whoCanModerateMembers = params.whoCanModerateMembers
|
||||
if (params.whoCanModerateContent !== undefined)
|
||||
body.whoCanModerateContent = params.whoCanModerateContent
|
||||
if (params.whoCanAssistContent !== undefined)
|
||||
body.whoCanAssistContent = params.whoCanAssistContent
|
||||
if (params.enableCollaborativeInbox !== undefined)
|
||||
body.enableCollaborativeInbox = params.enableCollaborativeInbox
|
||||
if (params.whoCanDiscoverGroup !== undefined)
|
||||
body.whoCanDiscoverGroup = params.whoCanDiscoverGroup
|
||||
if (params.defaultSender !== undefined) body.defaultSender = params.defaultSender
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error?.message || 'Failed to update group settings')
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
email: data.email ?? null,
|
||||
name: data.name ?? null,
|
||||
description: data.description ?? null,
|
||||
whoCanJoin: data.whoCanJoin ?? null,
|
||||
whoCanViewMembership: data.whoCanViewMembership ?? null,
|
||||
whoCanViewGroup: data.whoCanViewGroup ?? null,
|
||||
whoCanPostMessage: data.whoCanPostMessage ?? null,
|
||||
allowExternalMembers: data.allowExternalMembers ?? null,
|
||||
allowWebPosting: data.allowWebPosting ?? null,
|
||||
primaryLanguage: data.primaryLanguage ?? null,
|
||||
isArchived: data.isArchived ?? null,
|
||||
archiveOnly: data.archiveOnly ?? null,
|
||||
messageModerationLevel: data.messageModerationLevel ?? null,
|
||||
spamModerationLevel: data.spamModerationLevel ?? null,
|
||||
replyTo: data.replyTo ?? null,
|
||||
customReplyTo: data.customReplyTo ?? null,
|
||||
includeCustomFooter: data.includeCustomFooter ?? null,
|
||||
customFooterText: data.customFooterText ?? null,
|
||||
sendMessageDenyNotification: data.sendMessageDenyNotification ?? null,
|
||||
defaultMessageDenyNotificationText: data.defaultMessageDenyNotificationText ?? null,
|
||||
membersCanPostAsTheGroup: data.membersCanPostAsTheGroup ?? null,
|
||||
includeInGlobalAddressList: data.includeInGlobalAddressList ?? null,
|
||||
whoCanLeaveGroup: data.whoCanLeaveGroup ?? null,
|
||||
whoCanContactOwner: data.whoCanContactOwner ?? null,
|
||||
favoriteRepliesOnTop: data.favoriteRepliesOnTop ?? null,
|
||||
whoCanApproveMembers: data.whoCanApproveMembers ?? null,
|
||||
whoCanBanUsers: data.whoCanBanUsers ?? null,
|
||||
whoCanModerateMembers: data.whoCanModerateMembers ?? null,
|
||||
whoCanModerateContent: data.whoCanModerateContent ?? null,
|
||||
whoCanAssistContent: data.whoCanAssistContent ?? null,
|
||||
enableCollaborativeInbox: data.enableCollaborativeInbox ?? null,
|
||||
whoCanDiscoverGroup: data.whoCanDiscoverGroup ?? null,
|
||||
defaultSender: data.defaultSender ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
email: { type: 'string', description: "The group's email address" },
|
||||
name: { type: 'string', description: 'The group name' },
|
||||
description: { type: 'string', description: 'The group description' },
|
||||
whoCanJoin: { type: 'string', description: 'Who can join the group' },
|
||||
whoCanViewMembership: { type: 'string', description: 'Who can view group membership' },
|
||||
whoCanViewGroup: { type: 'string', description: 'Who can view group messages' },
|
||||
whoCanPostMessage: { type: 'string', description: 'Who can post messages to the group' },
|
||||
allowExternalMembers: { type: 'string', description: 'Whether external users can be members' },
|
||||
allowWebPosting: { type: 'string', description: 'Whether web posting is allowed' },
|
||||
primaryLanguage: { type: 'string', description: "The group's primary language" },
|
||||
isArchived: { type: 'string', description: 'Whether messages are archived' },
|
||||
archiveOnly: { type: 'string', description: 'Whether the group is archive-only' },
|
||||
messageModerationLevel: { type: 'string', description: 'Message moderation level' },
|
||||
spamModerationLevel: { type: 'string', description: 'Spam handling level' },
|
||||
replyTo: { type: 'string', description: 'Default reply destination' },
|
||||
customReplyTo: { type: 'string', description: 'Custom email for replies' },
|
||||
includeCustomFooter: { type: 'string', description: 'Whether to include custom footer' },
|
||||
customFooterText: { type: 'string', description: 'Custom footer text' },
|
||||
sendMessageDenyNotification: {
|
||||
type: 'string',
|
||||
description: 'Whether to send rejection notifications',
|
||||
},
|
||||
defaultMessageDenyNotificationText: {
|
||||
type: 'string',
|
||||
description: 'Default rejection message text',
|
||||
},
|
||||
membersCanPostAsTheGroup: {
|
||||
type: 'string',
|
||||
description: 'Whether members can post as the group',
|
||||
},
|
||||
includeInGlobalAddressList: {
|
||||
type: 'string',
|
||||
description: 'Whether included in Global Address List',
|
||||
},
|
||||
whoCanLeaveGroup: { type: 'string', description: 'Who can leave the group' },
|
||||
whoCanContactOwner: { type: 'string', description: 'Who can contact the group owner' },
|
||||
favoriteRepliesOnTop: { type: 'string', description: 'Whether favorite replies appear at top' },
|
||||
whoCanApproveMembers: { type: 'string', description: 'Who can approve new members' },
|
||||
whoCanBanUsers: { type: 'string', description: 'Who can ban users' },
|
||||
whoCanModerateMembers: { type: 'string', description: 'Who can manage members' },
|
||||
whoCanModerateContent: { type: 'string', description: 'Who can moderate content' },
|
||||
whoCanAssistContent: { type: 'string', description: 'Who can assist with content metadata' },
|
||||
enableCollaborativeInbox: {
|
||||
type: 'string',
|
||||
description: 'Whether collaborative inbox is enabled',
|
||||
},
|
||||
whoCanDiscoverGroup: { type: 'string', description: 'Who can discover the group' },
|
||||
defaultSender: { type: 'string', description: 'Default sender identity' },
|
||||
},
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
import type {
|
||||
GoogleSheetsAppendResponse,
|
||||
GoogleSheetsToolParams,
|
||||
GoogleSheetsV2AppendResponse,
|
||||
GoogleSheetsV2ToolParams,
|
||||
} from '@/tools/google_sheets/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
@@ -226,3 +228,197 @@ export const appendTool: ToolConfig<GoogleSheetsToolParams, GoogleSheetsAppendRe
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const appendV2Tool: ToolConfig<GoogleSheetsV2ToolParams, GoogleSheetsV2AppendResponse> = {
|
||||
id: 'google_sheets_append_v2',
|
||||
name: 'Append to Google Sheets V2',
|
||||
description: 'Append data to the end of a specific sheet in a Google Sheets spreadsheet',
|
||||
version: '2.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-sheets',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'The access token for the Google Sheets API',
|
||||
},
|
||||
spreadsheetId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'The ID of the spreadsheet to append to',
|
||||
},
|
||||
sheetName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The name of the sheet/tab to append to',
|
||||
},
|
||||
values: {
|
||||
type: 'array',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'The data to append as a 2D array (e.g. [["Alice", 30], ["Bob", 25]]) or array of objects.',
|
||||
},
|
||||
valueInputOption: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'hidden',
|
||||
description: 'The format of the data to append',
|
||||
},
|
||||
insertDataOption: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'hidden',
|
||||
description: 'How to insert the data (OVERWRITE or INSERT_ROWS)',
|
||||
},
|
||||
includeValuesInResponse: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'hidden',
|
||||
description: 'Whether to include the appended values in the response',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const sheetName = params.sheetName?.trim()
|
||||
if (!sheetName) {
|
||||
throw new Error('Sheet name is required')
|
||||
}
|
||||
|
||||
const url = new URL(
|
||||
`https://sheets.googleapis.com/v4/spreadsheets/${params.spreadsheetId}/values/${encodeURIComponent(sheetName)}:append`
|
||||
)
|
||||
|
||||
const valueInputOption = params.valueInputOption || 'USER_ENTERED'
|
||||
url.searchParams.append('valueInputOption', valueInputOption)
|
||||
|
||||
if (params.insertDataOption) {
|
||||
url.searchParams.append('insertDataOption', params.insertDataOption)
|
||||
}
|
||||
|
||||
if (params.includeValuesInResponse) {
|
||||
url.searchParams.append('includeValuesInResponse', 'true')
|
||||
}
|
||||
|
||||
return url.toString()
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
let processedValues: any = params.values || []
|
||||
|
||||
if (typeof processedValues === 'string') {
|
||||
try {
|
||||
processedValues = JSON.parse(processedValues)
|
||||
} catch (_error) {
|
||||
try {
|
||||
const sanitizedInput = (processedValues as string)
|
||||
.replace(/\n/g, '\\n')
|
||||
.replace(/\r/g, '\\r')
|
||||
.replace(/\t/g, '\\t')
|
||||
processedValues = JSON.parse(sanitizedInput)
|
||||
} catch (_secondError) {
|
||||
processedValues = [[processedValues]]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
Array.isArray(processedValues) &&
|
||||
processedValues.length > 0 &&
|
||||
typeof processedValues[0] === 'object' &&
|
||||
!Array.isArray(processedValues[0])
|
||||
) {
|
||||
const allKeys = new Set<string>()
|
||||
processedValues.forEach((obj: any) => {
|
||||
if (obj && typeof obj === 'object') {
|
||||
Object.keys(obj).forEach((key) => allKeys.add(key))
|
||||
}
|
||||
})
|
||||
const headers = Array.from(allKeys)
|
||||
|
||||
const rows = processedValues.map((obj: any) => {
|
||||
if (!obj || typeof obj !== 'object') {
|
||||
return Array(headers.length).fill('')
|
||||
}
|
||||
return headers.map((key) => {
|
||||
const value = obj[key]
|
||||
if (value !== null && typeof value === 'object') {
|
||||
return JSON.stringify(value)
|
||||
}
|
||||
return value === undefined ? '' : value
|
||||
})
|
||||
})
|
||||
|
||||
processedValues = [headers, ...rows]
|
||||
} else if (!Array.isArray(processedValues)) {
|
||||
processedValues = [[String(processedValues)]]
|
||||
} else if (!processedValues.every((item: any) => Array.isArray(item))) {
|
||||
processedValues = (processedValues as any[]).map((row: any) =>
|
||||
Array.isArray(row) ? row : [String(row)]
|
||||
)
|
||||
}
|
||||
|
||||
const body: Record<string, any> = {
|
||||
majorDimension: params.majorDimension || 'ROWS',
|
||||
values: processedValues,
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const urlParts = typeof response.url === 'string' ? response.url.split('/spreadsheets/') : []
|
||||
const spreadsheetId = urlParts[1]?.split('/')[0] || ''
|
||||
|
||||
const metadata = {
|
||||
spreadsheetId,
|
||||
spreadsheetUrl: `https://docs.google.com/spreadsheets/d/${spreadsheetId}`,
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
tableRange: data.tableRange ?? '',
|
||||
updatedRange: data.updates?.updatedRange ?? '',
|
||||
updatedRows: data.updates?.updatedRows ?? 0,
|
||||
updatedColumns: data.updates?.updatedColumns ?? 0,
|
||||
updatedCells: data.updates?.updatedCells ?? 0,
|
||||
metadata: {
|
||||
spreadsheetId: metadata.spreadsheetId,
|
||||
spreadsheetUrl: metadata.spreadsheetUrl,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
tableRange: { type: 'string', description: 'Range of the table where data was appended' },
|
||||
updatedRange: { type: 'string', description: 'Range of cells that were updated' },
|
||||
updatedRows: { type: 'number', description: 'Number of rows updated' },
|
||||
updatedColumns: { type: 'number', description: 'Number of columns updated' },
|
||||
updatedCells: { type: 'number', description: 'Number of cells updated' },
|
||||
metadata: {
|
||||
type: 'json',
|
||||
description: 'Spreadsheet metadata including ID and URL',
|
||||
properties: {
|
||||
spreadsheetId: { type: 'string', description: 'Google Sheets spreadsheet ID' },
|
||||
spreadsheetUrl: { type: 'string', description: 'Spreadsheet URL' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user