mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
Endpoint deployment v1
This commit is contained in:
420
apps/sim/app/api/tools/aws-lambda/deploy-endpoint/route.ts
Normal file
420
apps/sim/app/api/tools/aws-lambda/deploy-endpoint/route.ts
Normal file
@@ -0,0 +1,420 @@
|
||||
import {
|
||||
GetFunctionCommand,
|
||||
LambdaClient,
|
||||
AddPermissionCommand,
|
||||
} from '@aws-sdk/client-lambda'
|
||||
import {
|
||||
CreateApiCommand,
|
||||
GetApisCommand,
|
||||
CreateIntegrationCommand,
|
||||
CreateRouteCommand,
|
||||
CreateStageCommand,
|
||||
GetStagesCommand,
|
||||
ApiGatewayV2Client,
|
||||
} from '@aws-sdk/client-apigatewayv2'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { createLogger } from '@/lib/logs/console-logger'
|
||||
import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils'
|
||||
|
||||
const logger = createLogger('AWSLambdaDeployEndpointAPI')
|
||||
|
||||
// Validation schema for the request body
|
||||
const DeployEndpointRequestSchema = z.object({
|
||||
accessKeyId: z.string().min(1, 'AWS Access Key ID is required'),
|
||||
secretAccessKey: z.string().min(1, 'AWS Secret Access Key is required'),
|
||||
region: z.string().min(1, 'AWS Region is required'),
|
||||
functionName: z.string().min(1, 'Function name is required'),
|
||||
endpointName: z.string().min(1, 'Endpoint name is required'),
|
||||
role: z.string().min(1, 'Role ARN is required'),
|
||||
})
|
||||
|
||||
type DeployEndpointRequest = z.infer<typeof DeployEndpointRequestSchema>
|
||||
|
||||
interface DeployEndpointResponse {
|
||||
functionArn: string
|
||||
functionName: string
|
||||
endpointName: string
|
||||
endpointUrl: string
|
||||
region: string
|
||||
status: string
|
||||
lastModified: string
|
||||
apiGatewayId: string
|
||||
stageName: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a Lambda function exists
|
||||
*/
|
||||
async function checkFunctionExists(
|
||||
lambdaClient: LambdaClient,
|
||||
functionName: string
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
await lambdaClient.send(new GetFunctionCommand({ FunctionName: functionName }))
|
||||
return true
|
||||
} catch (error: any) {
|
||||
if (error.name === 'ResourceNotFoundException') {
|
||||
return false
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Lambda function details
|
||||
*/
|
||||
async function getFunctionDetails(
|
||||
lambdaClient: LambdaClient,
|
||||
functionName: string
|
||||
): Promise<any> {
|
||||
return await lambdaClient.send(new GetFunctionCommand({ FunctionName: functionName }))
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if API Gateway HTTP API already exists
|
||||
*/
|
||||
async function checkApiExists(
|
||||
apiGatewayClient: ApiGatewayV2Client,
|
||||
apiName: string
|
||||
): Promise<string | null> {
|
||||
try {
|
||||
const apis = await apiGatewayClient.send(new GetApisCommand({}))
|
||||
const existingApi = apis.Items?.find((api: any) => api.Name === apiName)
|
||||
return existingApi?.ApiId || null
|
||||
} catch (error) {
|
||||
logger.error('Error checking for existing API', { error })
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new API Gateway HTTP API
|
||||
*/
|
||||
async function createApiGateway(
|
||||
apiGatewayClient: ApiGatewayV2Client,
|
||||
apiName: string
|
||||
): Promise<string> {
|
||||
const createApiResponse = await apiGatewayClient.send(
|
||||
new CreateApiCommand({
|
||||
Name: apiName,
|
||||
ProtocolType: 'HTTP',
|
||||
Description: `HTTP API for Lambda function ${apiName}`,
|
||||
})
|
||||
)
|
||||
|
||||
if (!createApiResponse.ApiId) {
|
||||
throw new Error('Failed to create API Gateway - no ID returned')
|
||||
}
|
||||
|
||||
return createApiResponse.ApiId
|
||||
}
|
||||
|
||||
/**
|
||||
* Create API Gateway integration with Lambda
|
||||
*/
|
||||
async function createApiIntegration(
|
||||
apiGatewayClient: ApiGatewayV2Client,
|
||||
apiId: string,
|
||||
functionArn: string
|
||||
): Promise<string> {
|
||||
const integration = await apiGatewayClient.send(
|
||||
new CreateIntegrationCommand({
|
||||
ApiId: apiId,
|
||||
IntegrationType: 'AWS_PROXY',
|
||||
IntegrationUri: functionArn,
|
||||
IntegrationMethod: 'POST',
|
||||
PayloadFormatVersion: '2.0',
|
||||
})
|
||||
)
|
||||
|
||||
if (!integration.IntegrationId) {
|
||||
throw new Error('Failed to create integration - no ID returned')
|
||||
}
|
||||
|
||||
return integration.IntegrationId
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a route for the API Gateway
|
||||
*/
|
||||
async function createApiRoute(
|
||||
apiGatewayClient: ApiGatewayV2Client,
|
||||
apiId: string,
|
||||
integrationId: string
|
||||
): Promise<void> {
|
||||
await apiGatewayClient.send(
|
||||
new CreateRouteCommand({
|
||||
ApiId: apiId,
|
||||
RouteKey: 'ANY /',
|
||||
Target: `integrations/${integrationId}`,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Lambda permission for API Gateway
|
||||
*/
|
||||
async function addLambdaPermission(
|
||||
lambdaClient: LambdaClient,
|
||||
functionName: string,
|
||||
apiId: string,
|
||||
region: string,
|
||||
accountId: string
|
||||
): Promise<void> {
|
||||
try {
|
||||
await lambdaClient.send(
|
||||
new AddPermissionCommand({
|
||||
FunctionName: functionName,
|
||||
StatementId: `api-gateway-${apiId}`,
|
||||
Action: 'lambda:InvokeFunction',
|
||||
Principal: 'apigateway.amazonaws.com',
|
||||
SourceArn: `arn:aws:execute-api:${region}:${accountId}:${apiId}/*/*`,
|
||||
})
|
||||
)
|
||||
} catch (error: any) {
|
||||
// If permission already exists, that's fine
|
||||
if (error.name !== 'ResourceConflictException') {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a stage exists for the API Gateway
|
||||
*/
|
||||
async function checkStageExists(
|
||||
apiGatewayClient: ApiGatewayV2Client,
|
||||
apiId: string,
|
||||
stageName: string
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
const stages = await apiGatewayClient.send(
|
||||
new GetStagesCommand({
|
||||
ApiId: apiId,
|
||||
})
|
||||
)
|
||||
return stages.Items?.some((stage: any) => stage.StageName === stageName) || false
|
||||
} catch (error) {
|
||||
logger.error('Error checking for existing stage', { error })
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a stage for the API Gateway
|
||||
*/
|
||||
async function createApiStage(
|
||||
apiGatewayClient: ApiGatewayV2Client,
|
||||
apiId: string
|
||||
): Promise<string> {
|
||||
const stageName = 'prod'
|
||||
|
||||
// Check if stage already exists
|
||||
const stageExists = await checkStageExists(apiGatewayClient, apiId, stageName)
|
||||
|
||||
if (stageExists) {
|
||||
logger.info(`Stage ${stageName} already exists for API ${apiId}`)
|
||||
return stageName
|
||||
}
|
||||
|
||||
logger.info(`Creating new stage ${stageName} for API ${apiId}`)
|
||||
const stage = await apiGatewayClient.send(
|
||||
new CreateStageCommand({
|
||||
ApiId: apiId,
|
||||
StageName: stageName,
|
||||
AutoDeploy: true,
|
||||
})
|
||||
)
|
||||
|
||||
return stage.StageName || stageName
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure API is deployed by waiting for deployment to complete
|
||||
*/
|
||||
async function ensureApiDeployed(
|
||||
apiGatewayClient: ApiGatewayV2Client,
|
||||
apiId: string,
|
||||
stageName: string
|
||||
): Promise<void> {
|
||||
// In API Gateway v2, AutoDeploy: true should handle deployment automatically
|
||||
// But we can add a small delay to ensure the deployment completes
|
||||
await new Promise(resolve => setTimeout(resolve, 2000))
|
||||
|
||||
logger.info(`API Gateway deployment completed for API ${apiId}, stage ${stageName}`)
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const requestId = crypto.randomUUID().slice(0, 8)
|
||||
|
||||
try {
|
||||
logger.info(`[${requestId}] Processing AWS Lambda deploy endpoint request`)
|
||||
|
||||
// Parse and validate request body
|
||||
let body: any
|
||||
try {
|
||||
body = await request.json()
|
||||
} catch (parseError) {
|
||||
logger.error(`[${requestId}] Failed to parse request body`, {
|
||||
error: parseError instanceof Error ? parseError.message : String(parseError),
|
||||
})
|
||||
return createErrorResponse('Invalid JSON in request body', 400, 'INVALID_JSON')
|
||||
}
|
||||
|
||||
const validationResult = DeployEndpointRequestSchema.safeParse(body)
|
||||
if (!validationResult.success) {
|
||||
logger.warn(`[${requestId}] Invalid request body`, { errors: validationResult.error.errors })
|
||||
return createErrorResponse('Invalid request parameters', 400, 'VALIDATION_ERROR')
|
||||
}
|
||||
|
||||
const params = validationResult.data
|
||||
|
||||
// Log the deployment payload (excluding sensitive credentials)
|
||||
logger.info(`[${requestId}] AWS Lambda deploy endpoint payload received`, {
|
||||
functionName: params.functionName,
|
||||
endpointName: params.endpointName,
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId ? `${params.accessKeyId.substring(0, 4)}...` : undefined,
|
||||
hasSecretAccessKey: !!params.secretAccessKey,
|
||||
hasRole: !!params.role,
|
||||
role: params.role ? `${params.role.substring(0, 20)}...` : undefined,
|
||||
})
|
||||
|
||||
logger.info(`[${requestId}] Deploying Lambda function as endpoint: ${params.functionName}`)
|
||||
|
||||
// Create Lambda client
|
||||
const lambdaClient = new LambdaClient({
|
||||
region: params.region,
|
||||
credentials: {
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
},
|
||||
})
|
||||
|
||||
// Create API Gateway v2 client
|
||||
const apiGatewayClient = new ApiGatewayV2Client({
|
||||
region: params.region,
|
||||
credentials: {
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
},
|
||||
})
|
||||
|
||||
// Check if Lambda function exists
|
||||
const functionExists = await checkFunctionExists(lambdaClient, params.functionName)
|
||||
if (!functionExists) {
|
||||
logger.error(`[${requestId}] Lambda function ${params.functionName} does not exist`)
|
||||
return createErrorResponse(
|
||||
`Lambda function ${params.functionName} does not exist. Please deploy the function first.`,
|
||||
404,
|
||||
'FUNCTION_NOT_FOUND'
|
||||
)
|
||||
}
|
||||
|
||||
// Get function details
|
||||
const functionDetails = await getFunctionDetails(lambdaClient, params.functionName)
|
||||
const functionArn = functionDetails.Configuration?.FunctionArn
|
||||
|
||||
if (!functionArn) {
|
||||
logger.error(`[${requestId}] Failed to get function ARN for ${params.functionName}`)
|
||||
return createErrorResponse('Failed to get function ARN', 500, 'FUNCTION_ARN_ERROR')
|
||||
}
|
||||
|
||||
// Extract account ID from function ARN
|
||||
const accountId = functionArn.split(':')[4]
|
||||
if (!accountId) {
|
||||
logger.error(`[${requestId}] Failed to extract account ID from function ARN: ${functionArn}`)
|
||||
return createErrorResponse('Failed to extract account ID from function ARN', 500, 'ACCOUNT_ID_ERROR')
|
||||
}
|
||||
|
||||
// Check if API Gateway already exists
|
||||
let apiId = await checkApiExists(apiGatewayClient, params.endpointName)
|
||||
|
||||
if (!apiId) {
|
||||
logger.info(`[${requestId}] Creating new API Gateway HTTP API: ${params.endpointName}`)
|
||||
apiId = await createApiGateway(apiGatewayClient, params.endpointName)
|
||||
} else {
|
||||
logger.info(`[${requestId}] Using existing API Gateway HTTP API: ${params.endpointName} (${apiId})`)
|
||||
}
|
||||
|
||||
// Create API integration with Lambda
|
||||
logger.info(`[${requestId}] Creating API Gateway integration`)
|
||||
const integrationId = await createApiIntegration(apiGatewayClient, apiId, functionArn)
|
||||
|
||||
// Create route for the API
|
||||
logger.info(`[${requestId}] Creating API Gateway route`)
|
||||
await createApiRoute(apiGatewayClient, apiId, integrationId)
|
||||
|
||||
// Add Lambda permission for API Gateway
|
||||
logger.info(`[${requestId}] Adding Lambda permission for API Gateway`)
|
||||
await addLambdaPermission(lambdaClient, params.functionName, apiId, params.region, accountId)
|
||||
|
||||
// Create stage for the API Gateway
|
||||
logger.info(`[${requestId}] Creating API Gateway stage`)
|
||||
const stageName = await createApiStage(apiGatewayClient, apiId)
|
||||
|
||||
if (!stageName) {
|
||||
logger.error(`[${requestId}] Failed to create or get stage for API ${apiId}`)
|
||||
return createErrorResponse('Failed to create API Gateway stage', 500, 'STAGE_CREATION_ERROR')
|
||||
}
|
||||
|
||||
// Ensure API is deployed
|
||||
logger.info(`[${requestId}] Ensuring API Gateway deployment is complete`)
|
||||
await ensureApiDeployed(apiGatewayClient, apiId, stageName)
|
||||
|
||||
// Construct the endpoint URL
|
||||
const endpointUrl = `https://${apiId}.execute-api.${params.region}.amazonaws.com/${stageName}/`
|
||||
|
||||
const response: DeployEndpointResponse = {
|
||||
functionArn,
|
||||
functionName: params.functionName,
|
||||
endpointName: params.endpointName,
|
||||
endpointUrl,
|
||||
region: params.region,
|
||||
status: 'ACTIVE',
|
||||
lastModified: new Date().toISOString(),
|
||||
apiGatewayId: apiId,
|
||||
stageName,
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] Lambda function endpoint deployment completed successfully`, {
|
||||
functionName: params.functionName,
|
||||
endpointName: params.endpointName,
|
||||
endpointUrl,
|
||||
apiGatewayId: apiId,
|
||||
})
|
||||
|
||||
return createSuccessResponse({
|
||||
success: true,
|
||||
output: response,
|
||||
})
|
||||
} catch (error: any) {
|
||||
logger.error(`[${requestId}] Error deploying Lambda function endpoint`, {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
stack: error instanceof Error ? error.stack : undefined,
|
||||
})
|
||||
|
||||
// Handle specific AWS errors
|
||||
let errorMessage = 'Failed to deploy Lambda function endpoint'
|
||||
let statusCode = 500
|
||||
|
||||
if (error.name === 'AccessDeniedException') {
|
||||
errorMessage = 'Access denied. Please check your AWS credentials and permissions.'
|
||||
statusCode = 403
|
||||
} else if (error.name === 'InvalidParameterValueException') {
|
||||
errorMessage = `Invalid parameter: ${error.message}`
|
||||
statusCode = 400
|
||||
} else if (error.name === 'ResourceConflictException') {
|
||||
errorMessage = 'Resource conflict. The API may be in use or being updated.'
|
||||
statusCode = 409
|
||||
} else if (error.name === 'ServiceException') {
|
||||
errorMessage = 'AWS service error. Please try again later.'
|
||||
statusCode = 503
|
||||
} else if (error instanceof Error) {
|
||||
errorMessage = error.message
|
||||
}
|
||||
|
||||
return createErrorResponse(errorMessage, statusCode, 'DEPLOYMENT_ERROR')
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,8 @@ interface AWSLambdaResponse extends ToolResponse {
|
||||
output: {
|
||||
functionArn: string
|
||||
functionName: string
|
||||
endpointName?: string
|
||||
endpointUrl?: string
|
||||
runtime: string
|
||||
region: string
|
||||
status: string
|
||||
@@ -19,6 +21,8 @@ interface AWSLambdaResponse extends ToolResponse {
|
||||
tags: Record<string, string>
|
||||
codeFiles: Record<string, string>
|
||||
handler: string
|
||||
apiGatewayId?: string
|
||||
stageName?: string
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +45,7 @@ export const AWSLambdaBlock: BlockConfig<AWSLambdaResponse> = {
|
||||
options: [
|
||||
{ label: 'Fetch', id: 'fetch' },
|
||||
{ label: 'Create/Update', id: 'create/update' },
|
||||
{ label: 'Deploy Endpoint', id: 'deploy_endpoint' },
|
||||
{ label: 'Get Prompts', id: 'getPrompts' },
|
||||
],
|
||||
},
|
||||
@@ -54,7 +59,7 @@ export const AWSLambdaBlock: BlockConfig<AWSLambdaResponse> = {
|
||||
description: 'AWS Access Key ID for authentication. Required for all operations.',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['fetch', 'create/update'],
|
||||
value: ['fetch', 'create/update', 'deploy_endpoint'],
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -67,7 +72,7 @@ export const AWSLambdaBlock: BlockConfig<AWSLambdaResponse> = {
|
||||
description: 'AWS Secret Access Key for authentication. Required for all operations.',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['fetch', 'create/update'],
|
||||
value: ['fetch', 'create/update', 'deploy_endpoint'],
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -80,7 +85,7 @@ export const AWSLambdaBlock: BlockConfig<AWSLambdaResponse> = {
|
||||
description: 'IAM Role ARN that the Lambda function will assume during execution. Must have appropriate permissions.',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['fetch', 'create/update'],
|
||||
value: ['fetch', 'create/update', 'deploy_endpoint'],
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -114,7 +119,7 @@ export const AWSLambdaBlock: BlockConfig<AWSLambdaResponse> = {
|
||||
description: 'AWS region where the Lambda function will be deployed or is located.',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['fetch', 'create/update'],
|
||||
value: ['fetch', 'create/update', 'deploy_endpoint'],
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -126,7 +131,19 @@ export const AWSLambdaBlock: BlockConfig<AWSLambdaResponse> = {
|
||||
description: 'Name of the Lambda function. For fetch operations, this must be an existing function to understand its current state. For create/update, this will be the name of the new function or the existing function to update with any desired changes.',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['fetch', 'create/update'],
|
||||
value: ['fetch', 'create/update', 'deploy_endpoint'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'endpointName',
|
||||
title: 'Endpoint Name',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter API Gateway endpoint name',
|
||||
description: 'Name for the API Gateway HTTP API endpoint. This will be used to create the API Gateway and will appear in the endpoint URL.',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['deploy_endpoint'],
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -218,7 +235,7 @@ export const AWSLambdaBlock: BlockConfig<AWSLambdaResponse> = {
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: ['aws_lambda_deploy', 'aws_lambda_fetch', 'aws_lambda_get_prompts'],
|
||||
access: ['aws_lambda_deploy', 'aws_lambda_deploy_endpoint', 'aws_lambda_fetch', 'aws_lambda_get_prompts'],
|
||||
config: {
|
||||
tool: (params: Record<string, any>) => {
|
||||
const operation = String(params.operation || '').trim()
|
||||
@@ -226,6 +243,7 @@ export const AWSLambdaBlock: BlockConfig<AWSLambdaResponse> = {
|
||||
const operationMap: Record<string, string> = {
|
||||
fetch: 'aws_lambda_fetch',
|
||||
'create/update': 'aws_lambda_deploy',
|
||||
deploy_endpoint: 'aws_lambda_deploy_endpoint',
|
||||
getPrompts: 'aws_lambda_get_prompts',
|
||||
}
|
||||
if (operationMap[operation]) {
|
||||
@@ -235,6 +253,7 @@ export const AWSLambdaBlock: BlockConfig<AWSLambdaResponse> = {
|
||||
if (
|
||||
operation === 'aws_lambda_fetch' ||
|
||||
operation === 'aws_lambda_deploy' ||
|
||||
operation === 'aws_lambda_deploy_endpoint' ||
|
||||
operation === 'aws_lambda_get_prompts'
|
||||
) {
|
||||
return operation
|
||||
@@ -252,17 +271,20 @@ export const AWSLambdaBlock: BlockConfig<AWSLambdaResponse> = {
|
||||
role: { type: 'string', required: true },
|
||||
operation: { type: 'string', required: true },
|
||||
functionName: { type: 'string', required: true },
|
||||
handler: { type: 'string', required: true },
|
||||
runtime: { type: 'string', required: true },
|
||||
code: { type: 'json', required: true },
|
||||
timeout: { type: 'number', required: true },
|
||||
memorySize: { type: 'number', required: true },
|
||||
endpointName: { type: 'string', required: false },
|
||||
handler: { type: 'string', required: false },
|
||||
runtime: { type: 'string', required: false },
|
||||
code: { type: 'json', required: false },
|
||||
timeout: { type: 'number', required: false },
|
||||
memorySize: { type: 'number', required: false },
|
||||
environmentVariables: { type: 'json', required: false },
|
||||
tags: { type: 'json', required: false },
|
||||
},
|
||||
outputs: {
|
||||
functionArn: 'string',
|
||||
functionName: 'string',
|
||||
endpointName: 'any',
|
||||
endpointUrl: 'any',
|
||||
runtime: 'string',
|
||||
region: 'string',
|
||||
status: 'string',
|
||||
@@ -275,5 +297,7 @@ export const AWSLambdaBlock: BlockConfig<AWSLambdaResponse> = {
|
||||
tags: 'json',
|
||||
codeFiles: 'json',
|
||||
handler: 'string',
|
||||
apiGatewayId: 'any',
|
||||
stageName: 'any',
|
||||
},
|
||||
}
|
||||
|
||||
86
apps/sim/tools/aws_lambda/deploy_endpoint.ts
Normal file
86
apps/sim/tools/aws_lambda/deploy_endpoint.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import type { ToolConfig } from '../types'
|
||||
|
||||
interface AWSLambdaDeployEndpointParams {
|
||||
accessKeyId: string
|
||||
secretAccessKey: string
|
||||
region: string
|
||||
role: string
|
||||
functionName: string
|
||||
endpointName: string
|
||||
}
|
||||
|
||||
interface AWSLambdaDeployEndpointResponse {
|
||||
functionArn: string
|
||||
functionName: string
|
||||
endpointName: string
|
||||
endpointUrl: string
|
||||
region: string
|
||||
status: string
|
||||
lastModified: string
|
||||
apiGatewayId: string
|
||||
stageName: string
|
||||
}
|
||||
|
||||
export const awsLambdaDeployEndpointTool: ToolConfig<AWSLambdaDeployEndpointParams, AWSLambdaDeployEndpointResponse> = {
|
||||
id: 'aws_lambda_deploy_endpoint',
|
||||
name: 'AWS Lambda Deploy Endpoint',
|
||||
description: 'Deploy an AWS Lambda function as an HTTP endpoint using API Gateway. This tool creates or updates an API Gateway REST API and connects it to the specified Lambda function, making it accessible via HTTP requests. The endpoint will be publicly accessible and can handle GET, POST, PUT, DELETE, and other HTTP methods. This is useful for creating web APIs, webhooks, or any HTTP-based service using Lambda functions.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
// Common AWS parameters (always at the top)
|
||||
accessKeyId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
requiredForToolCall: true,
|
||||
description: 'AWS Access Key ID for authentication. This is required to access AWS services.',
|
||||
},
|
||||
secretAccessKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
requiredForToolCall: true,
|
||||
description: 'AWS Secret Access Key for authentication. This is required to access AWS services.',
|
||||
},
|
||||
region: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
requiredForToolCall: true,
|
||||
description: 'AWS region where the Lambda function and API Gateway will be deployed. Examples: us-east-1, eu-west-1, ap-southeast-2',
|
||||
},
|
||||
role: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
requiredForToolCall: true,
|
||||
description: 'IAM Role ARN that the Lambda function will assume during execution. This role must have appropriate permissions for the function to operate correctly and be invoked by API Gateway.',
|
||||
},
|
||||
// Operation-specific parameters
|
||||
functionName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
requiredForToolCall: true,
|
||||
description: 'Name of the existing Lambda function to deploy as an endpoint. This function must already exist in the specified region and be properly configured to handle HTTP requests.',
|
||||
},
|
||||
endpointName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
requiredForToolCall: true,
|
||||
description: 'Name for the API Gateway endpoint. This will be used to create the API Gateway REST API and will appear in the endpoint URL. Should be descriptive and unique within your AWS account.',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: '/api/tools/aws-lambda/deploy-endpoint',
|
||||
method: 'POST',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params: AWSLambdaDeployEndpointParams) => ({
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
region: params.region,
|
||||
role: params.role,
|
||||
functionName: params.functionName,
|
||||
endpointName: params.endpointName,
|
||||
}),
|
||||
},
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export { awsLambdaDeployTool } from './deploy'
|
||||
export { awsLambdaDeployEndpointTool } from './deploy_endpoint'
|
||||
export { awsLambdaFetchTool } from './fetch'
|
||||
export { awsLambdaGetPromptsTool } from './get_prompts'
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
airtableUpdateRecordTool,
|
||||
} from './airtable'
|
||||
import { autoblocksPromptManagerTool } from './autoblocks'
|
||||
import { awsLambdaDeployTool, awsLambdaFetchTool, awsLambdaGetPromptsTool } from './aws_lambda'
|
||||
import { awsLambdaDeployTool, awsLambdaDeployEndpointTool, awsLambdaFetchTool, awsLambdaGetPromptsTool } from './aws_lambda'
|
||||
import { browserUseRunTaskTool } from './browser_use'
|
||||
import { clayPopulateTool } from './clay'
|
||||
import { confluenceRetrieveTool, confluenceUpdateTool } from './confluence'
|
||||
@@ -225,6 +225,7 @@ export const tools: Record<string, ToolConfig> = {
|
||||
google_calendar_invite: googleCalendarInviteTool,
|
||||
workflow_executor: workflowExecutorTool,
|
||||
aws_lambda_deploy: awsLambdaDeployTool,
|
||||
aws_lambda_deploy_endpoint: awsLambdaDeployEndpointTool,
|
||||
aws_lambda_fetch: awsLambdaFetchTool,
|
||||
aws_lambda_get_prompts: awsLambdaGetPromptsTool,
|
||||
}
|
||||
|
||||
120
package-lock.json
generated
120
package-lock.json
generated
@@ -20813,6 +20813,126 @@
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/next-runtime-env/node_modules/@next/swc-darwin-arm64": {
|
||||
"version": "15.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.3.4.tgz",
|
||||
"integrity": "sha512-z0qIYTONmPRbwHWvpyrFXJd5F9YWLCsw3Sjrzj2ZvMYy9NPQMPZ1NjOJh4ojr4oQzcGYwgJKfidzehaNa1BpEg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/next-runtime-env/node_modules/@next/swc-darwin-x64": {
|
||||
"version": "15.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.3.4.tgz",
|
||||
"integrity": "sha512-Z0FYJM8lritw5Wq+vpHYuCIzIlEMjewG2aRkc3Hi2rcbULknYL/xqfpBL23jQnCSrDUGAo/AEv0Z+s2bff9Zkw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/next-runtime-env/node_modules/@next/swc-linux-arm64-gnu": {
|
||||
"version": "15.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.3.4.tgz",
|
||||
"integrity": "sha512-l8ZQOCCg7adwmsnFm8m5q9eIPAHdaB2F3cxhufYtVo84pymwKuWfpYTKcUiFcutJdp9xGHC+F1Uq3xnFU1B/7g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/next-runtime-env/node_modules/@next/swc-linux-arm64-musl": {
|
||||
"version": "15.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.3.4.tgz",
|
||||
"integrity": "sha512-wFyZ7X470YJQtpKot4xCY3gpdn8lE9nTlldG07/kJYexCUpX1piX+MBfZdvulo+t1yADFVEuzFfVHfklfEx8kw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/next-runtime-env/node_modules/@next/swc-linux-x64-gnu": {
|
||||
"version": "15.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.3.4.tgz",
|
||||
"integrity": "sha512-gEbH9rv9o7I12qPyvZNVTyP/PWKqOp8clvnoYZQiX800KkqsaJZuOXkWgMa7ANCCh/oEN2ZQheh3yH8/kWPSEg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/next-runtime-env/node_modules/@next/swc-linux-x64-musl": {
|
||||
"version": "15.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.3.4.tgz",
|
||||
"integrity": "sha512-Cf8sr0ufuC/nu/yQ76AnarbSAXcwG/wj+1xFPNbyNo8ltA6kw5d5YqO8kQuwVIxk13SBdtgXrNyom3ZosHAy4A==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/next-runtime-env/node_modules/@next/swc-win32-arm64-msvc": {
|
||||
"version": "15.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.3.4.tgz",
|
||||
"integrity": "sha512-ay5+qADDN3rwRbRpEhTOreOn1OyJIXS60tg9WMYTWCy3fB6rGoyjLVxc4dR9PYjEdR2iDYsaF5h03NA+XuYPQQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/next-runtime-env/node_modules/@next/swc-win32-x64-msvc": {
|
||||
"version": "15.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.3.4.tgz",
|
||||
"integrity": "sha512-4kDt31Bc9DGyYs41FTL1/kNpDeHyha2TC0j5sRRoKCyrhNcfZ/nRQkAUlF27mETwm8QyHqIjHJitfcza2Iykfg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user