Endpoint stuff v3

This commit is contained in:
Siddharth Ganesan
2025-07-07 20:02:03 -07:00
parent 4886e5aae8
commit 0ff86a1413
9 changed files with 124 additions and 72 deletions

View File

@@ -1,17 +1,13 @@
import {
GetFunctionCommand,
LambdaClient,
AddPermissionCommand,
} from '@aws-sdk/client-lambda'
import {
ApiGatewayV2Client,
CreateApiCommand,
GetApisCommand,
CreateIntegrationCommand,
CreateRouteCommand,
CreateStageCommand,
GetApisCommand,
GetStagesCommand,
ApiGatewayV2Client,
} from '@aws-sdk/client-apigatewayv2'
import { AddPermissionCommand, GetFunctionCommand, LambdaClient } from '@aws-sdk/client-lambda'
import type { NextRequest } from 'next/server'
import { z } from 'zod'
import { createLogger } from '@/lib/logs/console-logger'
@@ -64,10 +60,7 @@ async function checkFunctionExists(
/**
* Get Lambda function details
*/
async function getFunctionDetails(
lambdaClient: LambdaClient,
functionName: string
): Promise<any> {
async function getFunctionDetails(lambdaClient: LambdaClient, functionName: string): Promise<any> {
return await lambdaClient.send(new GetFunctionCommand({ FunctionName: functionName }))
}
@@ -102,11 +95,11 @@ async function createApiGateway(
Description: `HTTP API for Lambda function ${apiName}`,
})
)
if (!createApiResponse.ApiId) {
throw new Error('Failed to create API Gateway - no ID returned')
}
return createApiResponse.ApiId
}
@@ -127,11 +120,11 @@ async function createApiIntegration(
PayloadFormatVersion: '2.0',
})
)
if (!integration.IntegrationId) {
throw new Error('Failed to create integration - no ID returned')
}
return integration.IntegrationId
}
@@ -209,15 +202,15 @@ async function createApiStage(
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({
@@ -226,7 +219,7 @@ async function createApiStage(
AutoDeploy: true,
})
)
return stage.StageName || stageName
}
@@ -240,8 +233,8 @@ async function ensureApiDeployed(
): 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))
await new Promise((resolve) => setTimeout(resolve, 2000))
logger.info(`API Gateway deployment completed for API ${apiId}, stage ${stageName}`)
}
@@ -269,7 +262,7 @@ export async function POST(request: NextRequest) {
}
const params = validationResult.data
// Log the deployment payload (excluding sensitive credentials)
logger.info(`[${requestId}] AWS Lambda deploy endpoint payload received`, {
functionName: params.functionName,
@@ -280,7 +273,7 @@ export async function POST(request: NextRequest) {
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
@@ -325,17 +318,23 @@ export async function POST(request: NextRequest) {
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')
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})`)
logger.info(
`[${requestId}] Using existing API Gateway HTTP API: ${params.endpointName} (${apiId})`
)
}
// Create API integration with Lambda
@@ -353,7 +352,7 @@ export async function POST(request: NextRequest) {
// 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')
@@ -417,4 +416,4 @@ export async function POST(request: NextRequest) {
return createErrorResponse(errorMessage, statusCode, 'DEPLOYMENT_ERROR')
}
}
}

View File

@@ -252,7 +252,7 @@ export async function POST(request: NextRequest) {
}
const params = validationResult.data
// Log the deployment payload (excluding sensitive credentials)
logger.info(`[${requestId}] AWS Lambda deployment payload received`, {
functionName: params.functionName,
@@ -272,7 +272,7 @@ export async function POST(request: NextRequest) {
tags: params.tags,
tagsCount: Object.keys(params.tags || {}).length,
})
logger.info(`[${requestId}] Deploying Lambda function: ${params.functionName}`)
// Create Lambda client

View File

@@ -154,10 +154,10 @@ async function getFunctionDetailsWithCode(
if (functionCode.Code?.Location) {
try {
logger.info('Downloading code from:', functionCode.Code.Location)
const response = await fetch(functionCode.Code.Location)
logger.info('Fetch response status:', response.status)
if (response.ok) {
const zipBuffer = Buffer.from(await response.arrayBuffer())
logger.info('ZIP buffer size:', zipBuffer.length)
@@ -219,7 +219,7 @@ export async function POST(request: NextRequest) {
}
const params = validationResult.data
// Log the payload (excluding sensitive credentials)
logger.info(`[${requestId}] AWS Lambda fetch payload received`, {
functionName: params.functionName,
@@ -230,7 +230,7 @@ export async function POST(request: NextRequest) {
hasRole: !!params.role,
role: params.role ? `${params.role.substring(0, 20)}...` : undefined,
})
logger.info(`[${requestId}] Fetching Lambda function: ${params.functionName}`)
// Create Lambda client
@@ -265,8 +265,10 @@ export async function POST(request: NextRequest) {
} catch (fetchError: any) {
// Handle ResourceNotFoundException gracefully - return empty function details
if (fetchError.name === 'ResourceNotFoundException') {
logger.info(`[${requestId}] Lambda function '${params.functionName}' not found, returning empty response`)
logger.info(
`[${requestId}] Lambda function '${params.functionName}' not found, returning empty response`
)
const emptyFunctionDetails: LambdaFunctionDetails = {
functionArn: '',
functionName: params.functionName,
@@ -290,7 +292,7 @@ export async function POST(request: NextRequest) {
output: emptyFunctionDetails,
})
}
// Re-throw other errors to be handled by the outer catch block
throw fetchError
}

View File

@@ -82,7 +82,8 @@ export const AWSLambdaBlock: BlockConfig<AWSLambdaResponse> = {
layout: 'full',
placeholder: 'Enter the IAM Role ARN for Lambda execution',
password: false,
description: 'IAM Role ARN that the Lambda function will assume during execution. Must have appropriate permissions.',
description:
'IAM Role ARN that the Lambda function will assume during execution. Must have appropriate permissions.',
condition: {
field: 'operation',
value: ['fetch', 'create/update', 'deploy_endpoint'],
@@ -128,7 +129,8 @@ export const AWSLambdaBlock: BlockConfig<AWSLambdaResponse> = {
type: 'short-input',
layout: 'full',
placeholder: 'Enter Lambda function name',
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.',
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', 'deploy_endpoint'],
@@ -140,7 +142,8 @@ export const AWSLambdaBlock: BlockConfig<AWSLambdaResponse> = {
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.',
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'],
@@ -152,7 +155,8 @@ export const AWSLambdaBlock: BlockConfig<AWSLambdaResponse> = {
type: 'short-input',
layout: 'full',
placeholder: 'e.g., nodejs18.x, python3.11, java11',
description: 'Lambda runtime environment. Common values: nodejs18.x, python3.11, java11, go1.x, dotnet6, ruby2.7',
description:
'Lambda runtime environment. Common values: nodejs18.x, python3.11, java11, go1.x, dotnet6, ruby2.7',
condition: {
field: 'operation',
value: ['create/update'],
@@ -164,7 +168,8 @@ export const AWSLambdaBlock: BlockConfig<AWSLambdaResponse> = {
type: 'short-input',
layout: 'full',
placeholder: 'e.g., index.handler',
description: 'Function handler that Lambda calls to start execution. Format varies by runtime: index.handler (Node.js), lambda_function.lambda_handler (Python), etc.',
description:
'Function handler that Lambda calls to start execution. Format varies by runtime: index.handler (Node.js), lambda_function.lambda_handler (Python), etc.',
condition: {
field: 'operation',
value: ['create/update'],
@@ -188,7 +193,8 @@ export const AWSLambdaBlock: BlockConfig<AWSLambdaResponse> = {
type: 'short-input',
layout: 'half',
placeholder: 'Enter memory in MB (128-10240)',
description: 'Amount of memory allocated to the function in MB. Must be between 128 and 10240 MB.',
description:
'Amount of memory allocated to the function in MB. Must be between 128 and 10240 MB.',
condition: {
field: 'operation',
value: ['create/update'],
@@ -201,7 +207,8 @@ export const AWSLambdaBlock: BlockConfig<AWSLambdaResponse> = {
layout: 'full',
language: 'json',
placeholder: '{\n "index.js": "exports.handler = async (event) => {...};"\n}',
description: 'Function code files as JSON object. Keys are file paths, values are file contents. For Node.js, typically include index.js with the handler function.',
description:
'Function code files as JSON object. Keys are file paths, values are file contents. For Node.js, typically include index.js with the handler function.',
condition: {
field: 'operation',
value: ['create/update'],
@@ -214,7 +221,8 @@ export const AWSLambdaBlock: BlockConfig<AWSLambdaResponse> = {
layout: 'full',
columns: ['Key', 'Value'],
placeholder: 'Add environment variables as key-value pairs',
description: 'Environment variables that will be available to the Lambda function during execution.',
description:
'Environment variables that will be available to the Lambda function during execution.',
condition: {
field: 'operation',
value: ['create/update'],
@@ -235,7 +243,12 @@ export const AWSLambdaBlock: BlockConfig<AWSLambdaResponse> = {
},
],
tools: {
access: ['aws_lambda_deploy', 'aws_lambda_deploy_endpoint', '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()

View File

@@ -33,7 +33,8 @@ interface AWSLambdaDeployOutput {
export const awsLambdaDeployTool: ToolConfig<AWSLambdaDeployInput, AWSLambdaDeployOutput> = {
id: 'aws_lambda_deploy',
name: 'AWS Lambda Deploy',
description: 'Make sure to satisfy the user request.Deploy or update an AWS Lambda function with the specified configuration. This tool can create a new Lambda function or update an existing one with any changes you specify. It accepts function code as a JSON object where keys are file paths and values are file contents. For Node.js functions, typically include an index.js file with the handler function. The tool will package and deploy the code to AWS Lambda with the specified runtime, memory, timeout, and environment variables. When updating an existing function, this tool can make whatever changes you want to the function configuration and code.',
description:
'Make sure to satisfy the user request.Deploy or update an AWS Lambda function with the specified configuration. This tool can create a new Lambda function or update an existing one with any changes you specify. It accepts function code as a JSON object where keys are file paths and values are file contents. For Node.js functions, typically include an index.js file with the handler function. The tool will package and deploy the code to AWS Lambda with the specified runtime, memory, timeout, and environment variables. When updating an existing function, this tool can make whatever changes you want to the function configuration and code.',
version: '1.0.0',
params: {
@@ -48,38 +49,44 @@ export const awsLambdaDeployTool: ToolConfig<AWSLambdaDeployInput, AWSLambdaDepl
type: 'string',
required: true,
requiredForToolCall: true,
description: 'AWS Secret Access Key for authentication. This is required to access AWS services.',
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 will be deployed. Examples: us-east-1, eu-west-1, ap-southeast-2',
description:
'AWS region where the Lambda function 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.',
description:
'IAM Role ARN that the Lambda function will assume during execution. This role must have appropriate permissions for the function to operate correctly.',
},
// Operation-specific parameters
functionName: {
type: 'string',
required: true,
optionalToolInput: true,
description: 'Name of the Lambda function to create or update. If the function already exists, it will be updated with any changes you specify to the configuration and code.',
description:
'Name of the Lambda function to create or update. If the function already exists, it will be updated with any changes you specify to the configuration and code.',
},
handler: {
type: 'string',
required: true,
optionalToolInput: true,
description: 'Function handler that Lambda calls to start execution. Format varies by runtime: index.handler (Node.js), lambda_function.lambda_handler (Python), etc. If not provided, a default will be used based on the runtime.',
description:
'Function handler that Lambda calls to start execution. Format varies by runtime: index.handler (Node.js), lambda_function.lambda_handler (Python), etc. If not provided, a default will be used based on the runtime.',
},
runtime: {
type: 'string',
required: true,
optionalToolInput: true,
description: 'Lambda runtime environment. Common values: nodejs18.x, python3.11, java11, go1.x, dotnet6, ruby2.7. This determines the execution environment for your function.',
description:
'Lambda runtime environment. Common values: nodejs18.x, python3.11, java11, go1.x, dotnet6, ruby2.7. This determines the execution environment for your function.',
},
code: {
type: 'object',
@@ -92,28 +99,39 @@ export const awsLambdaDeployTool: ToolConfig<AWSLambdaDeployInput, AWSLambdaDepl
type: 'number',
required: true,
optionalToolInput: true,
description: 'Function timeout in seconds. Must be between 1 and 900 seconds (15 minutes). Default is 3 seconds.',
description:
'Function timeout in seconds. Must be between 1 and 900 seconds (15 minutes). Default is 3 seconds.',
default: 3,
},
memorySize: {
type: 'number',
required: true,
optionalToolInput: true,
description: 'Function memory size in MB. Must be between 128 and 10240 MB. More memory also means more CPU power. Default is 128 MB.',
description:
'Function memory size in MB. Must be between 128 and 10240 MB. More memory also means more CPU power. Default is 128 MB.',
default: 128,
},
environmentVariables: {
type: 'object',
required: false,
description: 'Environment variables for the function. These will be available to your function during execution. Example: {"API_KEY": "your-api-key", "ENVIRONMENT": "production"}.',
description:
'Environment variables for the function. These will be available to your function during execution. Example: {"API_KEY": "your-api-key", "ENVIRONMENT": "production"}.',
default: {},
},
tags: {
type: 'object',
required: false,
description: 'Tags for the function. Useful for organization and cost tracking. Example: {"Environment": "production", "Project": "my-app"}.',
description:
'Tags for the function. Useful for organization and cost tracking. Example: {"Environment": "production", "Project": "my-app"}.',
default: {},
},
endpointName: {
type: 'string',
required: true,
optionalToolInput: true,
description:
'Name of the API Gateway endpoint to create or update. This will be used to create the API Gateway and will appear in the endpoint URL.',
},
},
request: {

View File

@@ -21,10 +21,14 @@ interface AWSLambdaDeployEndpointResponse {
stageName: string
}
export const awsLambdaDeployEndpointTool: ToolConfig<AWSLambdaDeployEndpointParams, AWSLambdaDeployEndpointResponse> = {
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.',
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: {
@@ -39,32 +43,37 @@ export const awsLambdaDeployEndpointTool: ToolConfig<AWSLambdaDeployEndpointPara
type: 'string',
required: true,
requiredForToolCall: true,
description: 'AWS Secret Access Key for authentication. This is required to access AWS services.',
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',
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.',
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.',
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.',
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.',
},
},
@@ -83,4 +92,4 @@ export const awsLambdaDeployEndpointTool: ToolConfig<AWSLambdaDeployEndpointPara
endpointName: params.endpointName,
}),
},
}
}

View File

@@ -29,7 +29,8 @@ interface AWSLambdaFetchResponse {
export const awsLambdaFetchTool: ToolConfig<AWSLambdaFetchParams, AWSLambdaFetchResponse> = {
id: 'aws_lambda_fetch',
name: 'AWS Lambda Fetch',
description: 'Fetch AWS Lambda function details, configuration, and code files. Use this to retrieve information about an existing Lambda function including its runtime, handler, timeout, memory settings, environment variables, tags, and actual code files. This is used to understand the current state of a function before making changes. The fetch operation is read-only and does not modify the function.',
description:
'Fetch AWS Lambda function details, configuration, and code files. Use this to retrieve information about an existing Lambda function including its runtime, handler, timeout, memory settings, environment variables, tags, and actual code files. This is used to understand the current state of a function before making changes. The fetch operation is read-only and does not modify the function.',
version: '1.0.0',
params: {
@@ -44,26 +45,30 @@ export const awsLambdaFetchTool: ToolConfig<AWSLambdaFetchParams, AWSLambdaFetch
type: 'string',
required: true,
requiredForToolCall: true,
description: 'AWS Secret Access Key for authentication. This is required to access AWS services.',
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 is located. Examples: us-east-1, eu-west-1, ap-southeast-2',
description:
'AWS region where the Lambda function is located. 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.',
description:
'IAM Role ARN that the Lambda function will assume during execution. This role must have appropriate permissions for the function to operate correctly.',
},
// Operation-specific parameters
functionName: {
type: 'string',
required: true,
optionalToolInput: true,
description: 'Name of the existing Lambda function to fetch and understand. This must be the exact name of a function that already exists in the specified region. Use this to retrieve the current state before making changes.',
description:
'Name of the existing Lambda function to fetch and understand. This must be the exact name of a function that already exists in the specified region. Use this to retrieve the current state before making changes.',
},
},

View File

@@ -13,7 +13,8 @@ export const awsLambdaGetPromptsTool: ToolConfig<
> = {
id: 'aws_lambda_get_prompts',
name: 'AWS Lambda Get Prompts',
description: 'Get system prompt and schema for AWS Lambda operations. This tool provides AI assistance prompts and schemas to help with Lambda function development, including best practices, common patterns, and code examples.',
description:
'Get system prompt and schema for AWS Lambda operations. This tool provides AI assistance prompts and schemas to help with Lambda function development, including best practices, common patterns, and code examples.',
version: '1.0.0',
params: {

View File

@@ -5,7 +5,12 @@ import {
airtableUpdateRecordTool,
} from './airtable'
import { autoblocksPromptManagerTool } from './autoblocks'
import { awsLambdaDeployTool, awsLambdaDeployEndpointTool, awsLambdaFetchTool, awsLambdaGetPromptsTool } from './aws_lambda'
import {
awsLambdaDeployEndpointTool,
awsLambdaDeployTool,
awsLambdaFetchTool,
awsLambdaGetPromptsTool,
} from './aws_lambda'
import { browserUseRunTaskTool } from './browser_use'
import { clayPopulateTool } from './clay'
import { confluenceRetrieveTool, confluenceUpdateTool } from './confluence'