diff --git a/apps/docs/components/icons.tsx b/apps/docs/components/icons.tsx index 8adfadbf9a4..6a2a66183fb 100644 --- a/apps/docs/components/icons.tsx +++ b/apps/docs/components/icons.tsx @@ -124,6 +124,29 @@ export function ConditionalIcon(props: SVGProps) { ) } +export function CredentialIcon(props: SVGProps) { + return ( + + + + + + + ) +} + export function NoteIcon(props: SVGProps) { return ( ) { ) } +export function CloudFormationIcon(props: SVGProps) { + return ( + + + + + + ) +} + +export function CloudWatchIcon(props: SVGProps) { + return ( + + + + + + ) +} + export function TextractIcon(props: SVGProps) { return ( = { clay: ClayIcon, clerk: ClerkIcon, cloudflare: CloudflareIcon, + cloudformation: CloudFormationIcon, + cloudwatch: CloudWatchIcon, confluence_v2: ConfluenceIcon, cursor_v2: CursorIcon, databricks: DatabricksIcon, diff --git a/apps/docs/content/docs/en/tools/cloudformation.mdx b/apps/docs/content/docs/en/tools/cloudformation.mdx new file mode 100644 index 00000000000..e4db1981167 --- /dev/null +++ b/apps/docs/content/docs/en/tools/cloudformation.mdx @@ -0,0 +1,183 @@ +--- +title: CloudFormation +description: Manage and inspect AWS CloudFormation stacks, resources, and drift +--- + +import { BlockInfoCard } from "@/components/ui/block-info-card" + + + +{/* MANUAL-CONTENT-START:intro */} +[AWS CloudFormation](https://aws.amazon.com/cloudformation/) is an infrastructure-as-code service that lets you model, provision, and manage AWS resources by treating infrastructure as code. CloudFormation uses templates to describe the resources you need and their dependencies, so you can launch and configure them together as a stack. + +With the CloudFormation integration, you can: + +- **Describe Stacks**: List all stacks in a region or get detailed information about a specific stack, including its status, outputs, tags, and drift information +- **List Stack Resources**: Enumerate every resource in a stack with its logical ID, physical ID, type, status, and drift status +- **Describe Stack Events**: View the full event history for a stack to understand what happened during create, update, or delete operations +- **Detect Stack Drift**: Initiate drift detection to check whether any resources in a stack have been modified outside of CloudFormation +- **Drift Detection Status**: Poll the results of a drift detection operation to see which resources have drifted and how many +- **Get Template**: Retrieve the original template body (JSON or YAML) used to create or update a stack +- **Validate Template**: Check a CloudFormation template for syntax errors, required capabilities, parameters, and declared transforms before deploying + +In Sim, the CloudFormation integration enables your agents to monitor infrastructure state, detect configuration drift, audit stack resources, and validate templates as part of automated SRE and DevOps workflows. This is especially powerful when combined with CloudWatch for observability and SNS for alerting, creating end-to-end infrastructure monitoring pipelines. +{/* MANUAL-CONTENT-END */} + + +## Usage Instructions + +Integrate AWS CloudFormation into workflows. Describe stacks, list resources, detect drift, view stack events, retrieve templates, and validate templates. Requires AWS access key and secret access key. + + + +## Tools + +### `cloudformation_describe_stacks` + +List and describe CloudFormation stacks + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) | +| `awsAccessKeyId` | string | Yes | AWS access key ID | +| `awsSecretAccessKey` | string | Yes | AWS secret access key | +| `stackName` | string | No | Stack name or ID to describe \(omit to list all stacks\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `stacks` | array | List of CloudFormation stacks with status, outputs, and tags | + +### `cloudformation_list_stack_resources` + +List all resources in a CloudFormation stack + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) | +| `awsAccessKeyId` | string | Yes | AWS access key ID | +| `awsSecretAccessKey` | string | Yes | AWS secret access key | +| `stackName` | string | Yes | Stack name or ID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `resources` | array | List of stack resources with type, status, and drift information | + +### `cloudformation_detect_stack_drift` + +Initiate drift detection on a CloudFormation stack + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) | +| `awsAccessKeyId` | string | Yes | AWS access key ID | +| `awsSecretAccessKey` | string | Yes | AWS secret access key | +| `stackName` | string | Yes | Stack name or ID to detect drift on | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `stackDriftDetectionId` | string | ID to use with Describe Stack Drift Detection Status to check results | + +### `cloudformation_describe_stack_drift_detection_status` + +Check the status of a stack drift detection operation + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) | +| `awsAccessKeyId` | string | Yes | AWS access key ID | +| `awsSecretAccessKey` | string | Yes | AWS secret access key | +| `stackDriftDetectionId` | string | Yes | The drift detection ID returned by Detect Stack Drift | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `stackId` | string | The stack ID | +| `stackDriftDetectionId` | string | The drift detection ID | +| `stackDriftStatus` | string | Drift status \(DRIFTED, IN_SYNC, NOT_CHECKED\) | +| `detectionStatus` | string | Detection status \(DETECTION_IN_PROGRESS, DETECTION_COMPLETE, DETECTION_FAILED\) | +| `detectionStatusReason` | string | Reason if detection failed | +| `driftedStackResourceCount` | number | Number of resources that have drifted | +| `timestamp` | number | Timestamp of the detection | + +### `cloudformation_describe_stack_events` + +Get the event history for a CloudFormation stack + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) | +| `awsAccessKeyId` | string | Yes | AWS access key ID | +| `awsSecretAccessKey` | string | Yes | AWS secret access key | +| `stackName` | string | Yes | Stack name or ID | +| `limit` | number | No | Maximum number of events to return \(default: 50\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `events` | array | List of stack events with resource status and timestamps | + +### `cloudformation_get_template` + +Retrieve the template body for a CloudFormation stack + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) | +| `awsAccessKeyId` | string | Yes | AWS access key ID | +| `awsSecretAccessKey` | string | Yes | AWS secret access key | +| `stackName` | string | Yes | Stack name or ID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `templateBody` | string | The template body as a JSON or YAML string | +| `stagesAvailable` | array | Available template stages | + +### `cloudformation_validate_template` + +Validate a CloudFormation template for syntax and structural correctness + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) | +| `awsAccessKeyId` | string | Yes | AWS access key ID | +| `awsSecretAccessKey` | string | Yes | AWS secret access key | +| `templateBody` | string | Yes | The CloudFormation template body \(JSON or YAML\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `description` | string | Template description | +| `parameters` | array | Template parameters with defaults and descriptions | +| `capabilities` | array | Required capabilities \(e.g., CAPABILITY_IAM\) | +| `capabilitiesReason` | string | Reason capabilities are required | +| `declaredTransforms` | array | Transforms used in the template \(e.g., AWS::Serverless-2016-10-31\) | + + diff --git a/apps/docs/content/docs/en/tools/cloudwatch.mdx b/apps/docs/content/docs/en/tools/cloudwatch.mdx new file mode 100644 index 00000000000..a3c5757a87a --- /dev/null +++ b/apps/docs/content/docs/en/tools/cloudwatch.mdx @@ -0,0 +1,180 @@ +--- +title: CloudWatch +description: Query and monitor AWS CloudWatch logs, metrics, and alarms +--- + +import { BlockInfoCard } from "@/components/ui/block-info-card" + + + +## Usage Instructions + +Integrate AWS CloudWatch into workflows. Run Log Insights queries, list log groups, retrieve log events, list and get metrics, and monitor alarms. Requires AWS access key and secret access key. + + + +## Tools + +### `cloudwatch_query_logs` + +Run a CloudWatch Log Insights query against one or more log groups + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) | +| `awsAccessKeyId` | string | Yes | AWS access key ID | +| `awsSecretAccessKey` | string | Yes | AWS secret access key | +| `logGroupNames` | array | Yes | Log group names to query | +| `queryString` | string | Yes | CloudWatch Log Insights query string | +| `startTime` | number | Yes | Start time as Unix epoch seconds | +| `endTime` | number | Yes | End time as Unix epoch seconds | +| `limit` | number | No | Maximum number of results to return | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `results` | array | Query result rows | +| `statistics` | object | Query statistics \(bytesScanned, recordsMatched, recordsScanned\) | +| `status` | string | Query completion status | + +### `cloudwatch_describe_log_groups` + +List available CloudWatch log groups + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) | +| `awsAccessKeyId` | string | Yes | AWS access key ID | +| `awsSecretAccessKey` | string | Yes | AWS secret access key | +| `prefix` | string | No | Filter log groups by name prefix | +| `limit` | number | No | Maximum number of log groups to return | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `logGroups` | array | List of CloudWatch log groups with metadata | + +### `cloudwatch_get_log_events` + +Retrieve log events from a specific CloudWatch log stream + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) | +| `awsAccessKeyId` | string | Yes | AWS access key ID | +| `awsSecretAccessKey` | string | Yes | AWS secret access key | +| `logGroupName` | string | Yes | CloudWatch log group name | +| `logStreamName` | string | Yes | CloudWatch log stream name | +| `startTime` | number | No | Start time as Unix epoch seconds | +| `endTime` | number | No | End time as Unix epoch seconds | +| `limit` | number | No | Maximum number of events to return | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `events` | array | Log events with timestamp, message, and ingestion time | + +### `cloudwatch_describe_log_streams` + +List log streams within a CloudWatch log group + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) | +| `awsAccessKeyId` | string | Yes | AWS access key ID | +| `awsSecretAccessKey` | string | Yes | AWS secret access key | +| `logGroupName` | string | Yes | CloudWatch log group name | +| `prefix` | string | No | Filter log streams by name prefix | +| `limit` | number | No | Maximum number of log streams to return | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `logStreams` | array | List of log streams with metadata | + +### `cloudwatch_list_metrics` + +List available CloudWatch metrics + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) | +| `awsAccessKeyId` | string | Yes | AWS access key ID | +| `awsSecretAccessKey` | string | Yes | AWS secret access key | +| `namespace` | string | No | Filter by namespace \(e.g., AWS/EC2, AWS/Lambda\) | +| `metricName` | string | No | Filter by metric name | +| `recentlyActive` | boolean | No | Only show metrics active in the last 3 hours | +| `limit` | number | No | Maximum number of metrics to return | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `metrics` | array | List of metrics with namespace, name, and dimensions | + +### `cloudwatch_get_metric_statistics` + +Get statistics for a CloudWatch metric over a time range + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) | +| `awsAccessKeyId` | string | Yes | AWS access key ID | +| `awsSecretAccessKey` | string | Yes | AWS secret access key | +| `namespace` | string | Yes | Metric namespace \(e.g., AWS/EC2, AWS/Lambda\) | +| `metricName` | string | Yes | Metric name \(e.g., CPUUtilization, Invocations\) | +| `startTime` | number | Yes | Start time as Unix epoch seconds | +| `endTime` | number | Yes | End time as Unix epoch seconds | +| `period` | number | Yes | Granularity in seconds \(e.g., 60, 300, 3600\) | +| `statistics` | array | Yes | Statistics to retrieve \(Average, Sum, Minimum, Maximum, SampleCount\) | +| `dimensions` | string | No | Dimensions as JSON \(e.g., \{"InstanceId": "i-1234"\}\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `label` | string | Metric label | +| `datapoints` | array | Datapoints with timestamp and statistics values | + +### `cloudwatch_describe_alarms` + +List and filter CloudWatch alarms + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) | +| `awsAccessKeyId` | string | Yes | AWS access key ID | +| `awsSecretAccessKey` | string | Yes | AWS secret access key | +| `alarmNamePrefix` | string | No | Filter alarms by name prefix | +| `stateValue` | string | No | Filter by alarm state \(OK, ALARM, INSUFFICIENT_DATA\) | +| `alarmType` | string | No | Filter by alarm type \(MetricAlarm, CompositeAlarm\) | +| `limit` | number | No | Maximum number of alarms to return | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `alarms` | array | List of CloudWatch alarms with state and configuration | + + diff --git a/apps/docs/content/docs/en/tools/meta.json b/apps/docs/content/docs/en/tools/meta.json index 220f7a351cb..cc194da1f25 100644 --- a/apps/docs/content/docs/en/tools/meta.json +++ b/apps/docs/content/docs/en/tools/meta.json @@ -23,6 +23,8 @@ "clay", "clerk", "cloudflare", + "cloudformation", + "cloudwatch", "confluence", "cursor", "databricks", diff --git a/apps/sim/app/(landing)/integrations/data/icon-mapping.ts b/apps/sim/app/(landing)/integrations/data/icon-mapping.ts index 4e78266631b..ee5f8c95a5b 100644 --- a/apps/sim/app/(landing)/integrations/data/icon-mapping.ts +++ b/apps/sim/app/(landing)/integrations/data/icon-mapping.ts @@ -27,7 +27,9 @@ import { CirclebackIcon, ClayIcon, ClerkIcon, + CloudFormationIcon, CloudflareIcon, + CloudWatchIcon, ConfluenceIcon, CursorIcon, DatabricksIcon, @@ -211,6 +213,8 @@ export const blockTypeToIconMap: Record = { clay: ClayIcon, clerk: ClerkIcon, cloudflare: CloudflareIcon, + cloudformation: CloudFormationIcon, + cloudwatch: CloudWatchIcon, confluence_v2: ConfluenceIcon, cursor_v2: CursorIcon, databricks: DatabricksIcon, diff --git a/apps/sim/app/(landing)/integrations/data/integrations.json b/apps/sim/app/(landing)/integrations/data/integrations.json index 4cbd8dd5da1..32881c28d13 100644 --- a/apps/sim/app/(landing)/integrations/data/integrations.json +++ b/apps/sim/app/(landing)/integrations/data/integrations.json @@ -1912,6 +1912,100 @@ "integrationType": "developer-tools", "tags": ["cloud", "monitoring"] }, + { + "type": "cloudformation", + "slug": "cloudformation", + "name": "CloudFormation", + "description": "Manage and inspect AWS CloudFormation stacks, resources, and drift", + "longDescription": "Integrate AWS CloudFormation into workflows. Describe stacks, list resources, detect drift, view stack events, retrieve templates, and validate templates. Requires AWS access key and secret access key.", + "bgColor": "linear-gradient(45deg, #B0084D 0%, #FF4F8B 100%)", + "iconName": "CloudFormationIcon", + "docsUrl": "https://docs.sim.ai/tools/cloudformation", + "operations": [ + { + "name": "Describe Stacks", + "description": "List and describe CloudFormation stacks" + }, + { + "name": "List Stack Resources", + "description": "List all resources in a CloudFormation stack" + }, + { + "name": "Describe Stack Events", + "description": "Get the event history for a CloudFormation stack" + }, + { + "name": "Detect Stack Drift", + "description": "Initiate drift detection on a CloudFormation stack" + }, + { + "name": "Drift Detection Status", + "description": "Check the status of a stack drift detection operation" + }, + { + "name": "Get Template", + "description": "Retrieve the template body for a CloudFormation stack" + }, + { + "name": "Validate Template", + "description": "Validate a CloudFormation template for syntax and structural correctness" + } + ], + "operationCount": 7, + "triggers": [], + "triggerCount": 0, + "authType": "none", + "category": "tools", + "integrationType": "developer-tools", + "tags": ["cloud"] + }, + { + "type": "cloudwatch", + "slug": "cloudwatch", + "name": "CloudWatch", + "description": "Query and monitor AWS CloudWatch logs, metrics, and alarms", + "longDescription": "Integrate AWS CloudWatch into workflows. Run Log Insights queries, list log groups, retrieve log events, list and get metrics, and monitor alarms. Requires AWS access key and secret access key.", + "bgColor": "linear-gradient(45deg, #B0084D 0%, #FF4F8B 100%)", + "iconName": "CloudWatchIcon", + "docsUrl": "https://docs.sim.ai/tools/cloudwatch", + "operations": [ + { + "name": "Query Logs (Insights)", + "description": "Run a CloudWatch Log Insights query against one or more log groups" + }, + { + "name": "Describe Log Groups", + "description": "List available CloudWatch log groups" + }, + { + "name": "Get Log Events", + "description": "Retrieve log events from a specific CloudWatch log stream" + }, + { + "name": "Describe Log Streams", + "description": "List log streams within a CloudWatch log group" + }, + { + "name": "List Metrics", + "description": "List available CloudWatch metrics" + }, + { + "name": "Get Metric Statistics", + "description": "Get statistics for a CloudWatch metric over a time range" + }, + { + "name": "Describe Alarms", + "description": "List and filter CloudWatch alarms" + } + ], + "operationCount": 7, + "triggers": [], + "triggerCount": 0, + "authType": "none", + "category": "tools", + "integrationType": "analytics", + "tags": ["cloud", "monitoring"] + }, { "type": "confluence_v2", "slug": "confluence", diff --git a/apps/sim/app/api/tools/cloudformation/describe-stack-drift-detection-status/route.ts b/apps/sim/app/api/tools/cloudformation/describe-stack-drift-detection-status/route.ts new file mode 100644 index 00000000000..b47611b0cfb --- /dev/null +++ b/apps/sim/app/api/tools/cloudformation/describe-stack-drift-detection-status/route.ts @@ -0,0 +1,61 @@ +import { + CloudFormationClient, + DescribeStackDriftDetectionStatusCommand, +} from '@aws-sdk/client-cloudformation' +import { createLogger } from '@sim/logger' +import { type NextRequest, NextResponse } from 'next/server' +import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' + +const logger = createLogger('CloudFormationDescribeStackDriftDetectionStatus') + +const DescribeStackDriftDetectionStatusSchema = z.object({ + region: z.string().min(1, 'AWS region is required'), + accessKeyId: z.string().min(1, 'AWS access key ID is required'), + secretAccessKey: z.string().min(1, 'AWS secret access key is required'), + stackDriftDetectionId: z.string().min(1, 'Stack drift detection ID is required'), +}) + +export async function POST(request: NextRequest) { + try { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + + const body = await request.json() + const validatedData = DescribeStackDriftDetectionStatusSchema.parse(body) + + const client = new CloudFormationClient({ + region: validatedData.region, + credentials: { + accessKeyId: validatedData.accessKeyId, + secretAccessKey: validatedData.secretAccessKey, + }, + }) + + const command = new DescribeStackDriftDetectionStatusCommand({ + StackDriftDetectionId: validatedData.stackDriftDetectionId, + }) + + const response = await client.send(command) + + return NextResponse.json({ + success: true, + output: { + stackId: response.StackId ?? '', + stackDriftDetectionId: response.StackDriftDetectionId ?? '', + stackDriftStatus: response.StackDriftStatus, + detectionStatus: response.DetectionStatus ?? 'UNKNOWN', + detectionStatusReason: response.DetectionStatusReason, + driftedStackResourceCount: response.DriftedStackResourceCount, + timestamp: response.Timestamp?.getTime(), + }, + }) + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : 'Failed to describe stack drift detection status' + logger.error('DescribeStackDriftDetectionStatus failed', { error: errorMessage }) + return NextResponse.json({ error: errorMessage }, { status: 500 }) + } +} diff --git a/apps/sim/app/api/tools/cloudformation/describe-stack-events/route.ts b/apps/sim/app/api/tools/cloudformation/describe-stack-events/route.ts new file mode 100644 index 00000000000..b838512a437 --- /dev/null +++ b/apps/sim/app/api/tools/cloudformation/describe-stack-events/route.ts @@ -0,0 +1,78 @@ +import { + CloudFormationClient, + DescribeStackEventsCommand, + type StackEvent, +} from '@aws-sdk/client-cloudformation' +import { createLogger } from '@sim/logger' +import { type NextRequest, NextResponse } from 'next/server' +import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' + +const logger = createLogger('CloudFormationDescribeStackEvents') + +const DescribeStackEventsSchema = z.object({ + region: z.string().min(1, 'AWS region is required'), + accessKeyId: z.string().min(1, 'AWS access key ID is required'), + secretAccessKey: z.string().min(1, 'AWS secret access key is required'), + stackName: z.string().min(1, 'Stack name is required'), + limit: z.preprocess( + (v) => (v === '' || v === undefined || v === null ? undefined : v), + z.number({ coerce: true }).int().positive().optional() + ), +}) + +export async function POST(request: NextRequest) { + try { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + + const body = await request.json() + const validatedData = DescribeStackEventsSchema.parse(body) + + const client = new CloudFormationClient({ + region: validatedData.region, + credentials: { + accessKeyId: validatedData.accessKeyId, + secretAccessKey: validatedData.secretAccessKey, + }, + }) + + const limit = validatedData.limit ?? 50 + + const allEvents: StackEvent[] = [] + let nextToken: string | undefined + do { + const command = new DescribeStackEventsCommand({ + StackName: validatedData.stackName, + ...(nextToken && { NextToken: nextToken }), + }) + const response = await client.send(command) + allEvents.push(...(response.StackEvents ?? [])) + nextToken = allEvents.length >= limit ? undefined : response.NextToken + } while (nextToken) + + const events = allEvents.slice(0, limit).map((e) => ({ + stackId: e.StackId ?? '', + eventId: e.EventId ?? '', + stackName: e.StackName ?? '', + logicalResourceId: e.LogicalResourceId, + physicalResourceId: e.PhysicalResourceId, + resourceType: e.ResourceType, + resourceStatus: e.ResourceStatus, + resourceStatusReason: e.ResourceStatusReason, + timestamp: e.Timestamp?.getTime(), + })) + + return NextResponse.json({ + success: true, + output: { events }, + }) + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : 'Failed to describe CloudFormation stack events' + logger.error('DescribeStackEvents failed', { error: errorMessage }) + return NextResponse.json({ error: errorMessage }, { status: 500 }) + } +} diff --git a/apps/sim/app/api/tools/cloudformation/describe-stacks/route.ts b/apps/sim/app/api/tools/cloudformation/describe-stacks/route.ts new file mode 100644 index 00000000000..30cf207d7e6 --- /dev/null +++ b/apps/sim/app/api/tools/cloudformation/describe-stacks/route.ts @@ -0,0 +1,86 @@ +import { + CloudFormationClient, + DescribeStacksCommand, + type Stack, +} from '@aws-sdk/client-cloudformation' +import { createLogger } from '@sim/logger' +import { type NextRequest, NextResponse } from 'next/server' +import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' + +const logger = createLogger('CloudFormationDescribeStacks') + +const DescribeStacksSchema = z.object({ + region: z.string().min(1, 'AWS region is required'), + accessKeyId: z.string().min(1, 'AWS access key ID is required'), + secretAccessKey: z.string().min(1, 'AWS secret access key is required'), + stackName: z.string().optional(), +}) + +export async function POST(request: NextRequest) { + try { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + + const body = await request.json() + const validatedData = DescribeStacksSchema.parse(body) + + const client = new CloudFormationClient({ + region: validatedData.region, + credentials: { + accessKeyId: validatedData.accessKeyId, + secretAccessKey: validatedData.secretAccessKey, + }, + }) + + const allStacks: Stack[] = [] + let nextToken: string | undefined + do { + const command = new DescribeStacksCommand({ + ...(validatedData.stackName && { StackName: validatedData.stackName }), + ...(nextToken && { NextToken: nextToken }), + }) + const response = await client.send(command) + allStacks.push(...(response.Stacks ?? [])) + nextToken = response.NextToken + } while (nextToken) + + const stacks = allStacks.map((s) => ({ + stackName: s.StackName ?? '', + stackId: s.StackId ?? '', + stackStatus: s.StackStatus ?? 'UNKNOWN', + stackStatusReason: s.StackStatusReason, + creationTime: s.CreationTime?.getTime(), + lastUpdatedTime: s.LastUpdatedTime?.getTime(), + description: s.Description, + enableTerminationProtection: s.EnableTerminationProtection, + driftInformation: s.DriftInformation + ? { + stackDriftStatus: s.DriftInformation.StackDriftStatus, + lastCheckTimestamp: s.DriftInformation.LastCheckTimestamp?.getTime(), + } + : null, + outputs: (s.Outputs ?? []).map((o) => ({ + outputKey: o.OutputKey ?? '', + outputValue: o.OutputValue ?? '', + description: o.Description, + })), + tags: (s.Tags ?? []).map((t) => ({ + key: t.Key ?? '', + value: t.Value ?? '', + })), + })) + + return NextResponse.json({ + success: true, + output: { stacks }, + }) + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : 'Failed to describe CloudFormation stacks' + logger.error('DescribeStacks failed', { error: errorMessage }) + return NextResponse.json({ error: errorMessage }, { status: 500 }) + } +} diff --git a/apps/sim/app/api/tools/cloudformation/detect-stack-drift/route.ts b/apps/sim/app/api/tools/cloudformation/detect-stack-drift/route.ts new file mode 100644 index 00000000000..30d05166816 --- /dev/null +++ b/apps/sim/app/api/tools/cloudformation/detect-stack-drift/route.ts @@ -0,0 +1,56 @@ +import { CloudFormationClient, DetectStackDriftCommand } from '@aws-sdk/client-cloudformation' +import { createLogger } from '@sim/logger' +import { type NextRequest, NextResponse } from 'next/server' +import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' + +const logger = createLogger('CloudFormationDetectStackDrift') + +const DetectStackDriftSchema = z.object({ + region: z.string().min(1, 'AWS region is required'), + accessKeyId: z.string().min(1, 'AWS access key ID is required'), + secretAccessKey: z.string().min(1, 'AWS secret access key is required'), + stackName: z.string().min(1, 'Stack name is required'), +}) + +export async function POST(request: NextRequest) { + try { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + + const body = await request.json() + const validatedData = DetectStackDriftSchema.parse(body) + + const client = new CloudFormationClient({ + region: validatedData.region, + credentials: { + accessKeyId: validatedData.accessKeyId, + secretAccessKey: validatedData.secretAccessKey, + }, + }) + + const command = new DetectStackDriftCommand({ + StackName: validatedData.stackName, + }) + + const response = await client.send(command) + + if (!response.StackDriftDetectionId) { + throw new Error('No drift detection ID returned') + } + + return NextResponse.json({ + success: true, + output: { + stackDriftDetectionId: response.StackDriftDetectionId, + }, + }) + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : 'Failed to detect CloudFormation stack drift' + logger.error('DetectStackDrift failed', { error: errorMessage }) + return NextResponse.json({ error: errorMessage }, { status: 500 }) + } +} diff --git a/apps/sim/app/api/tools/cloudformation/get-template/route.ts b/apps/sim/app/api/tools/cloudformation/get-template/route.ts new file mode 100644 index 00000000000..9abdad4e170 --- /dev/null +++ b/apps/sim/app/api/tools/cloudformation/get-template/route.ts @@ -0,0 +1,53 @@ +import { CloudFormationClient, GetTemplateCommand } from '@aws-sdk/client-cloudformation' +import { createLogger } from '@sim/logger' +import { type NextRequest, NextResponse } from 'next/server' +import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' + +const logger = createLogger('CloudFormationGetTemplate') + +const GetTemplateSchema = z.object({ + region: z.string().min(1, 'AWS region is required'), + accessKeyId: z.string().min(1, 'AWS access key ID is required'), + secretAccessKey: z.string().min(1, 'AWS secret access key is required'), + stackName: z.string().min(1, 'Stack name is required'), +}) + +export async function POST(request: NextRequest) { + try { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + + const body = await request.json() + const validatedData = GetTemplateSchema.parse(body) + + const client = new CloudFormationClient({ + region: validatedData.region, + credentials: { + accessKeyId: validatedData.accessKeyId, + secretAccessKey: validatedData.secretAccessKey, + }, + }) + + const command = new GetTemplateCommand({ + StackName: validatedData.stackName, + }) + + const response = await client.send(command) + + return NextResponse.json({ + success: true, + output: { + templateBody: response.TemplateBody ?? '', + stagesAvailable: response.StagesAvailable ?? [], + }, + }) + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : 'Failed to get CloudFormation template' + logger.error('GetTemplate failed', { error: errorMessage }) + return NextResponse.json({ error: errorMessage }, { status: 500 }) + } +} diff --git a/apps/sim/app/api/tools/cloudformation/list-stack-resources/route.ts b/apps/sim/app/api/tools/cloudformation/list-stack-resources/route.ts new file mode 100644 index 00000000000..ca22c8e8567 --- /dev/null +++ b/apps/sim/app/api/tools/cloudformation/list-stack-resources/route.ts @@ -0,0 +1,75 @@ +import { + CloudFormationClient, + ListStackResourcesCommand, + type StackResourceSummary, +} from '@aws-sdk/client-cloudformation' +import { createLogger } from '@sim/logger' +import { type NextRequest, NextResponse } from 'next/server' +import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' + +const logger = createLogger('CloudFormationListStackResources') + +const ListStackResourcesSchema = z.object({ + region: z.string().min(1, 'AWS region is required'), + accessKeyId: z.string().min(1, 'AWS access key ID is required'), + secretAccessKey: z.string().min(1, 'AWS secret access key is required'), + stackName: z.string().min(1, 'Stack name is required'), +}) + +export async function POST(request: NextRequest) { + try { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + + const body = await request.json() + const validatedData = ListStackResourcesSchema.parse(body) + + const client = new CloudFormationClient({ + region: validatedData.region, + credentials: { + accessKeyId: validatedData.accessKeyId, + secretAccessKey: validatedData.secretAccessKey, + }, + }) + + const allSummaries: StackResourceSummary[] = [] + let nextToken: string | undefined + do { + const command = new ListStackResourcesCommand({ + StackName: validatedData.stackName, + ...(nextToken && { NextToken: nextToken }), + }) + const response = await client.send(command) + allSummaries.push(...(response.StackResourceSummaries ?? [])) + nextToken = response.NextToken + } while (nextToken) + + const resources = allSummaries.map((r) => ({ + logicalResourceId: r.LogicalResourceId ?? '', + physicalResourceId: r.PhysicalResourceId, + resourceType: r.ResourceType ?? '', + resourceStatus: r.ResourceStatus ?? 'UNKNOWN', + resourceStatusReason: r.ResourceStatusReason, + lastUpdatedTimestamp: r.LastUpdatedTimestamp?.getTime(), + driftInformation: r.DriftInformation + ? { + stackResourceDriftStatus: r.DriftInformation.StackResourceDriftStatus, + lastCheckTimestamp: r.DriftInformation.LastCheckTimestamp?.getTime(), + } + : null, + })) + + return NextResponse.json({ + success: true, + output: { resources }, + }) + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : 'Failed to list CloudFormation stack resources' + logger.error('ListStackResources failed', { error: errorMessage }) + return NextResponse.json({ error: errorMessage }, { status: 500 }) + } +} diff --git a/apps/sim/app/api/tools/cloudformation/validate-template/route.ts b/apps/sim/app/api/tools/cloudformation/validate-template/route.ts new file mode 100644 index 00000000000..e2a8b4428ed --- /dev/null +++ b/apps/sim/app/api/tools/cloudformation/validate-template/route.ts @@ -0,0 +1,61 @@ +import { CloudFormationClient, ValidateTemplateCommand } from '@aws-sdk/client-cloudformation' +import { createLogger } from '@sim/logger' +import { type NextRequest, NextResponse } from 'next/server' +import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' + +const logger = createLogger('CloudFormationValidateTemplate') + +const ValidateTemplateSchema = z.object({ + region: z.string().min(1, 'AWS region is required'), + accessKeyId: z.string().min(1, 'AWS access key ID is required'), + secretAccessKey: z.string().min(1, 'AWS secret access key is required'), + templateBody: z.string().min(1, 'Template body is required'), +}) + +export async function POST(request: NextRequest) { + try { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + + const body = await request.json() + const validatedData = ValidateTemplateSchema.parse(body) + + const client = new CloudFormationClient({ + region: validatedData.region, + credentials: { + accessKeyId: validatedData.accessKeyId, + secretAccessKey: validatedData.secretAccessKey, + }, + }) + + const command = new ValidateTemplateCommand({ + TemplateBody: validatedData.templateBody, + }) + + const response = await client.send(command) + + return NextResponse.json({ + success: true, + output: { + description: response.Description, + parameters: (response.Parameters ?? []).map((p) => ({ + parameterKey: p.ParameterKey, + defaultValue: p.DefaultValue, + noEcho: p.NoEcho, + description: p.Description, + })), + capabilities: response.Capabilities ?? [], + capabilitiesReason: response.CapabilitiesReason, + declaredTransforms: response.DeclaredTransforms ?? [], + }, + }) + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : 'Failed to validate CloudFormation template' + logger.error('ValidateTemplate failed', { error: errorMessage }) + return NextResponse.json({ error: errorMessage }, { status: 500 }) + } +} diff --git a/apps/sim/app/api/tools/cloudwatch/get-metric-statistics/route.ts b/apps/sim/app/api/tools/cloudwatch/get-metric-statistics/route.ts index 1a510d3f12f..321092b3283 100644 --- a/apps/sim/app/api/tools/cloudwatch/get-metric-statistics/route.ts +++ b/apps/sim/app/api/tools/cloudwatch/get-metric-statistics/route.ts @@ -72,7 +72,7 @@ export async function POST(request: NextRequest) { const datapoints = (response.Datapoints ?? []) .sort((a, b) => (a.Timestamp?.getTime() ?? 0) - (b.Timestamp?.getTime() ?? 0)) .map((dp) => ({ - timestamp: dp.Timestamp ? Math.floor(dp.Timestamp.getTime() / 1000) : 0, + timestamp: dp.Timestamp ? dp.Timestamp.getTime() : 0, average: dp.Average, sum: dp.Sum, minimum: dp.Minimum, diff --git a/apps/sim/app/api/tools/cloudwatch/list-metrics/route.ts b/apps/sim/app/api/tools/cloudwatch/list-metrics/route.ts index ce2cbf80f9f..09485cb8590 100644 --- a/apps/sim/app/api/tools/cloudwatch/list-metrics/route.ts +++ b/apps/sim/app/api/tools/cloudwatch/list-metrics/route.ts @@ -37,15 +37,18 @@ export async function POST(request: NextRequest) { }, }) + const limit = validatedData.limit ?? 500 + const command = new ListMetricsCommand({ ...(validatedData.namespace && { Namespace: validatedData.namespace }), ...(validatedData.metricName && { MetricName: validatedData.metricName }), ...(validatedData.recentlyActive && { RecentlyActive: 'PT3H' }), + ...(limit <= 500 && { MaxResults: limit }), }) const response = await client.send(command) - const metrics = (response.Metrics ?? []).slice(0, validatedData.limit ?? 500).map((m) => ({ + const metrics = (response.Metrics ?? []).slice(0, limit).map((m) => ({ namespace: m.Namespace ?? '', metricName: m.MetricName ?? '', dimensions: (m.Dimensions ?? []).map((d) => ({ diff --git a/apps/sim/blocks/blocks/cloudformation.ts b/apps/sim/blocks/blocks/cloudformation.ts new file mode 100644 index 00000000000..d99fabebac2 --- /dev/null +++ b/apps/sim/blocks/blocks/cloudformation.ts @@ -0,0 +1,329 @@ +import { CloudFormationIcon } from '@/components/icons' +import type { BlockConfig } from '@/blocks/types' +import { IntegrationType } from '@/blocks/types' +import type { + CloudFormationDescribeStackDriftDetectionStatusResponse, + CloudFormationDescribeStackEventsResponse, + CloudFormationDescribeStacksResponse, + CloudFormationDetectStackDriftResponse, + CloudFormationGetTemplateResponse, + CloudFormationListStackResourcesResponse, + CloudFormationValidateTemplateResponse, +} from '@/tools/cloudformation/types' + +export const CloudFormationBlock: BlockConfig< + | CloudFormationDescribeStacksResponse + | CloudFormationListStackResourcesResponse + | CloudFormationDetectStackDriftResponse + | CloudFormationDescribeStackDriftDetectionStatusResponse + | CloudFormationDescribeStackEventsResponse + | CloudFormationGetTemplateResponse + | CloudFormationValidateTemplateResponse +> = { + type: 'cloudformation', + name: 'CloudFormation', + description: 'Manage and inspect AWS CloudFormation stacks, resources, and drift', + longDescription: + 'Integrate AWS CloudFormation into workflows. Describe stacks, list resources, detect drift, view stack events, retrieve templates, and validate templates. Requires AWS access key and secret access key.', + category: 'tools', + integrationType: IntegrationType.DeveloperTools, + tags: ['cloud'], + docsLink: 'https://docs.sim.ai/tools/cloudformation', + bgColor: 'linear-gradient(45deg, #B0084D 0%, #FF4F8B 100%)', + icon: CloudFormationIcon, + subBlocks: [ + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + options: [ + { label: 'Describe Stacks', id: 'describe_stacks' }, + { label: 'List Stack Resources', id: 'list_stack_resources' }, + { label: 'Describe Stack Events', id: 'describe_stack_events' }, + { label: 'Detect Stack Drift', id: 'detect_stack_drift' }, + { label: 'Drift Detection Status', id: 'describe_stack_drift_detection_status' }, + { label: 'Get Template', id: 'get_template' }, + { label: 'Validate Template', id: 'validate_template' }, + ], + value: () => 'describe_stacks', + }, + { + id: 'awsRegion', + title: 'AWS Region', + type: 'short-input', + placeholder: 'us-east-1', + required: true, + }, + { + id: 'awsAccessKeyId', + title: 'AWS Access Key ID', + type: 'short-input', + placeholder: 'AKIA...', + password: true, + required: true, + }, + { + id: 'awsSecretAccessKey', + title: 'AWS Secret Access Key', + type: 'short-input', + placeholder: 'Your secret access key', + password: true, + required: true, + }, + { + id: 'stackName', + title: 'Stack Name', + type: 'short-input', + placeholder: 'my-stack or arn:aws:cloudformation:...', + condition: { + field: 'operation', + value: [ + 'describe_stacks', + 'list_stack_resources', + 'describe_stack_events', + 'detect_stack_drift', + 'get_template', + ], + }, + required: { + field: 'operation', + value: [ + 'list_stack_resources', + 'describe_stack_events', + 'detect_stack_drift', + 'get_template', + ], + }, + }, + { + id: 'stackDriftDetectionId', + title: 'Drift Detection ID', + type: 'short-input', + placeholder: 'ID from Detect Stack Drift output', + condition: { field: 'operation', value: 'describe_stack_drift_detection_status' }, + required: { field: 'operation', value: 'describe_stack_drift_detection_status' }, + }, + { + id: 'templateBody', + title: 'Template Body', + type: 'code', + placeholder: '{\n "AWSTemplateFormatVersion": "2010-09-09",\n "Resources": { ... }\n}', + condition: { field: 'operation', value: 'validate_template' }, + required: { field: 'operation', value: 'validate_template' }, + }, + { + id: 'limit', + title: 'Limit', + type: 'short-input', + placeholder: '50', + condition: { field: 'operation', value: 'describe_stack_events' }, + }, + ], + tools: { + access: [ + 'cloudformation_describe_stacks', + 'cloudformation_list_stack_resources', + 'cloudformation_detect_stack_drift', + 'cloudformation_describe_stack_drift_detection_status', + 'cloudformation_describe_stack_events', + 'cloudformation_get_template', + 'cloudformation_validate_template', + ], + config: { + tool: (params) => { + switch (params.operation) { + case 'describe_stacks': + return 'cloudformation_describe_stacks' + case 'list_stack_resources': + return 'cloudformation_list_stack_resources' + case 'detect_stack_drift': + return 'cloudformation_detect_stack_drift' + case 'describe_stack_drift_detection_status': + return 'cloudformation_describe_stack_drift_detection_status' + case 'describe_stack_events': + return 'cloudformation_describe_stack_events' + case 'get_template': + return 'cloudformation_get_template' + case 'validate_template': + return 'cloudformation_validate_template' + default: + throw new Error(`Invalid CloudFormation operation: ${params.operation}`) + } + }, + params: (params) => { + const { operation, limit, ...rest } = params + + const awsRegion = rest.awsRegion + const awsAccessKeyId = rest.awsAccessKeyId + const awsSecretAccessKey = rest.awsSecretAccessKey + const parsedLimit = limit ? Number.parseInt(String(limit), 10) : undefined + + switch (operation) { + case 'describe_stacks': + return { + awsRegion, + awsAccessKeyId, + awsSecretAccessKey, + ...(rest.stackName && { stackName: rest.stackName }), + } + + case 'list_stack_resources': { + if (!rest.stackName) { + throw new Error('Stack name is required') + } + return { + awsRegion, + awsAccessKeyId, + awsSecretAccessKey, + stackName: rest.stackName, + } + } + + case 'detect_stack_drift': { + if (!rest.stackName) { + throw new Error('Stack name is required') + } + return { + awsRegion, + awsAccessKeyId, + awsSecretAccessKey, + stackName: rest.stackName, + } + } + + case 'describe_stack_drift_detection_status': { + if (!rest.stackDriftDetectionId) { + throw new Error('Drift detection ID is required') + } + return { + awsRegion, + awsAccessKeyId, + awsSecretAccessKey, + stackDriftDetectionId: rest.stackDriftDetectionId, + } + } + + case 'describe_stack_events': { + if (!rest.stackName) { + throw new Error('Stack name is required') + } + return { + awsRegion, + awsAccessKeyId, + awsSecretAccessKey, + stackName: rest.stackName, + ...(parsedLimit !== undefined && { limit: parsedLimit }), + } + } + + case 'get_template': { + if (!rest.stackName) { + throw new Error('Stack name is required') + } + return { + awsRegion, + awsAccessKeyId, + awsSecretAccessKey, + stackName: rest.stackName, + } + } + + case 'validate_template': { + if (!rest.templateBody) { + throw new Error('Template body is required') + } + return { + awsRegion, + awsAccessKeyId, + awsSecretAccessKey, + templateBody: rest.templateBody, + } + } + + default: + throw new Error(`Invalid CloudFormation operation: ${operation}`) + } + }, + }, + }, + inputs: { + operation: { type: 'string', description: 'CloudFormation operation to perform' }, + awsRegion: { type: 'string', description: 'AWS region' }, + awsAccessKeyId: { type: 'string', description: 'AWS access key ID' }, + awsSecretAccessKey: { type: 'string', description: 'AWS secret access key' }, + stackName: { type: 'string', description: 'Stack name or ID' }, + stackDriftDetectionId: { type: 'string', description: 'Drift detection ID' }, + templateBody: { type: 'string', description: 'CloudFormation template body (JSON or YAML)' }, + limit: { type: 'number', description: 'Maximum number of results' }, + }, + outputs: { + stacks: { + type: 'array', + description: 'List of CloudFormation stacks with status, outputs, and tags', + }, + resources: { + type: 'array', + description: 'List of stack resources with type, status, and drift info', + }, + events: { + type: 'array', + description: 'Stack events with resource status and timestamps', + }, + stackDriftDetectionId: { + type: 'string', + description: 'Drift detection ID for checking status', + }, + stackId: { + type: 'string', + description: 'Stack ID', + }, + stackDriftStatus: { + type: 'string', + description: 'Drift status (DRIFTED, IN_SYNC, NOT_CHECKED)', + }, + detectionStatus: { + type: 'string', + description: 'Detection status (DETECTION_IN_PROGRESS, DETECTION_COMPLETE, DETECTION_FAILED)', + }, + detectionStatusReason: { + type: 'string', + description: 'Reason if detection failed', + }, + driftedStackResourceCount: { + type: 'number', + description: 'Number of drifted resources', + }, + timestamp: { + type: 'number', + description: 'Detection timestamp', + }, + templateBody: { + type: 'string', + description: 'Template body (JSON or YAML)', + }, + stagesAvailable: { + type: 'array', + description: 'Available template stages', + }, + description: { + type: 'string', + description: 'Template description', + }, + parameters: { + type: 'array', + description: 'Template parameters', + }, + capabilities: { + type: 'array', + description: 'Required capabilities', + }, + capabilitiesReason: { + type: 'string', + description: 'Reason capabilities are required', + }, + declaredTransforms: { + type: 'array', + description: 'Transforms used in the template (e.g., AWS::Serverless-2016-10-31)', + }, + }, +} diff --git a/apps/sim/blocks/registry.ts b/apps/sim/blocks/registry.ts index c977522afd4..8b21cebea1a 100644 --- a/apps/sim/blocks/registry.ts +++ b/apps/sim/blocks/registry.ts @@ -24,6 +24,7 @@ import { CirclebackBlock } from '@/blocks/blocks/circleback' import { ClayBlock } from '@/blocks/blocks/clay' import { ClerkBlock } from '@/blocks/blocks/clerk' import { CloudflareBlock } from '@/blocks/blocks/cloudflare' +import { CloudFormationBlock } from '@/blocks/blocks/cloudformation' import { CloudWatchBlock } from '@/blocks/blocks/cloudwatch' import { ConditionBlock } from '@/blocks/blocks/condition' import { ConfluenceBlock, ConfluenceV2Block } from '@/blocks/blocks/confluence' @@ -242,6 +243,7 @@ export const registry: Record = { chat_trigger: ChatTriggerBlock, circleback: CirclebackBlock, cloudflare: CloudflareBlock, + cloudformation: CloudFormationBlock, cloudwatch: CloudWatchBlock, clay: ClayBlock, clerk: ClerkBlock, diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx index 25004d5da66..6a2a66183fb 100644 --- a/apps/sim/components/icons.tsx +++ b/apps/sim/components/icons.tsx @@ -4653,6 +4653,32 @@ export function SQSIcon(props: SVGProps) { ) } +export function CloudFormationIcon(props: SVGProps) { + return ( + + + + + + ) +} + export function CloudWatchIcon(props: SVGProps) { return ( ) { transform='translate(40, 40) scale(1.25) translate(-40, -40)' > diff --git a/apps/sim/package.json b/apps/sim/package.json index 5f3dc02b499..36704047dff 100644 --- a/apps/sim/package.json +++ b/apps/sim/package.json @@ -37,6 +37,7 @@ "@a2a-js/sdk": "0.3.7", "@anthropic-ai/sdk": "0.71.2", "@aws-sdk/client-bedrock-runtime": "3.940.0", + "@aws-sdk/client-cloudformation": "3.1019.0", "@aws-sdk/client-cloudwatch": "3.940.0", "@aws-sdk/client-cloudwatch-logs": "3.940.0", "@aws-sdk/client-dynamodb": "3.940.0", diff --git a/apps/sim/tools/cloudformation/describe_stack_drift_detection_status.ts b/apps/sim/tools/cloudformation/describe_stack_drift_detection_status.ts new file mode 100644 index 00000000000..5d3cfc4cbda --- /dev/null +++ b/apps/sim/tools/cloudformation/describe_stack_drift_detection_status.ts @@ -0,0 +1,96 @@ +import type { + CloudFormationDescribeStackDriftDetectionStatusParams, + CloudFormationDescribeStackDriftDetectionStatusResponse, +} from '@/tools/cloudformation/types' +import type { ToolConfig } from '@/tools/types' + +export const describeStackDriftDetectionStatusTool: ToolConfig< + CloudFormationDescribeStackDriftDetectionStatusParams, + CloudFormationDescribeStackDriftDetectionStatusResponse +> = { + id: 'cloudformation_describe_stack_drift_detection_status', + name: 'CloudFormation Describe Stack Drift Detection Status', + description: 'Check the status of a stack drift detection operation', + version: '1.0', + + params: { + awsRegion: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'AWS region (e.g., us-east-1)', + }, + awsAccessKeyId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'AWS access key ID', + }, + awsSecretAccessKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'AWS secret access key', + }, + stackDriftDetectionId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The drift detection ID returned by Detect Stack Drift', + }, + }, + + request: { + url: '/api/tools/cloudformation/describe-stack-drift-detection-status', + method: 'POST', + headers: () => ({ + 'Content-Type': 'application/json', + }), + body: (params) => ({ + region: params.awsRegion, + accessKeyId: params.awsAccessKeyId, + secretAccessKey: params.awsSecretAccessKey, + stackDriftDetectionId: params.stackDriftDetectionId, + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + if (!response.ok) { + throw new Error(data.error || 'Failed to describe stack drift detection status') + } + + return { + success: true, + output: { + stackId: data.output.stackId, + stackDriftDetectionId: data.output.stackDriftDetectionId, + stackDriftStatus: data.output.stackDriftStatus, + detectionStatus: data.output.detectionStatus, + detectionStatusReason: data.output.detectionStatusReason, + driftedStackResourceCount: data.output.driftedStackResourceCount, + timestamp: data.output.timestamp, + }, + } + }, + + outputs: { + stackId: { type: 'string', description: 'The stack ID' }, + stackDriftDetectionId: { type: 'string', description: 'The drift detection ID' }, + stackDriftStatus: { + type: 'string', + description: 'Drift status (DRIFTED, IN_SYNC, NOT_CHECKED)', + }, + detectionStatus: { + type: 'string', + description: 'Detection status (DETECTION_IN_PROGRESS, DETECTION_COMPLETE, DETECTION_FAILED)', + }, + detectionStatusReason: { type: 'string', description: 'Reason if detection failed' }, + driftedStackResourceCount: { + type: 'number', + description: 'Number of resources that have drifted', + }, + timestamp: { type: 'number', description: 'Timestamp of the detection' }, + }, +} diff --git a/apps/sim/tools/cloudformation/describe_stack_events.ts b/apps/sim/tools/cloudformation/describe_stack_events.ts new file mode 100644 index 00000000000..043bcc07b49 --- /dev/null +++ b/apps/sim/tools/cloudformation/describe_stack_events.ts @@ -0,0 +1,85 @@ +import type { + CloudFormationDescribeStackEventsParams, + CloudFormationDescribeStackEventsResponse, +} from '@/tools/cloudformation/types' +import type { ToolConfig } from '@/tools/types' + +export const describeStackEventsTool: ToolConfig< + CloudFormationDescribeStackEventsParams, + CloudFormationDescribeStackEventsResponse +> = { + id: 'cloudformation_describe_stack_events', + name: 'CloudFormation Describe Stack Events', + description: 'Get the event history for a CloudFormation stack', + version: '1.0', + + params: { + awsRegion: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'AWS region (e.g., us-east-1)', + }, + awsAccessKeyId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'AWS access key ID', + }, + awsSecretAccessKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'AWS secret access key', + }, + stackName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Stack name or ID', + }, + limit: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Maximum number of events to return (default: 50)', + }, + }, + + request: { + url: '/api/tools/cloudformation/describe-stack-events', + method: 'POST', + headers: () => ({ + 'Content-Type': 'application/json', + }), + body: (params) => ({ + region: params.awsRegion, + accessKeyId: params.awsAccessKeyId, + secretAccessKey: params.awsSecretAccessKey, + stackName: params.stackName, + ...(params.limit !== undefined && { limit: params.limit }), + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + if (!response.ok) { + throw new Error(data.error || 'Failed to describe CloudFormation stack events') + } + + return { + success: true, + output: { + events: data.output.events, + }, + } + }, + + outputs: { + events: { + type: 'array', + description: 'List of stack events with resource status and timestamps', + }, + }, +} diff --git a/apps/sim/tools/cloudformation/describe_stacks.ts b/apps/sim/tools/cloudformation/describe_stacks.ts new file mode 100644 index 00000000000..6bb67d0733f --- /dev/null +++ b/apps/sim/tools/cloudformation/describe_stacks.ts @@ -0,0 +1,78 @@ +import type { + CloudFormationDescribeStacksParams, + CloudFormationDescribeStacksResponse, +} from '@/tools/cloudformation/types' +import type { ToolConfig } from '@/tools/types' + +export const describeStacksTool: ToolConfig< + CloudFormationDescribeStacksParams, + CloudFormationDescribeStacksResponse +> = { + id: 'cloudformation_describe_stacks', + name: 'CloudFormation Describe Stacks', + description: 'List and describe CloudFormation stacks', + version: '1.0', + + params: { + awsRegion: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'AWS region (e.g., us-east-1)', + }, + awsAccessKeyId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'AWS access key ID', + }, + awsSecretAccessKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'AWS secret access key', + }, + stackName: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Stack name or ID to describe (omit to list all stacks)', + }, + }, + + request: { + url: '/api/tools/cloudformation/describe-stacks', + method: 'POST', + headers: () => ({ + 'Content-Type': 'application/json', + }), + body: (params) => ({ + region: params.awsRegion, + accessKeyId: params.awsAccessKeyId, + secretAccessKey: params.awsSecretAccessKey, + ...(params.stackName && { stackName: params.stackName }), + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + if (!response.ok) { + throw new Error(data.error || 'Failed to describe CloudFormation stacks') + } + + return { + success: true, + output: { + stacks: data.output.stacks, + }, + } + }, + + outputs: { + stacks: { + type: 'array', + description: 'List of CloudFormation stacks with status, outputs, and tags', + }, + }, +} diff --git a/apps/sim/tools/cloudformation/detect_stack_drift.ts b/apps/sim/tools/cloudformation/detect_stack_drift.ts new file mode 100644 index 00000000000..23de02ef9dd --- /dev/null +++ b/apps/sim/tools/cloudformation/detect_stack_drift.ts @@ -0,0 +1,78 @@ +import type { + CloudFormationDetectStackDriftParams, + CloudFormationDetectStackDriftResponse, +} from '@/tools/cloudformation/types' +import type { ToolConfig } from '@/tools/types' + +export const detectStackDriftTool: ToolConfig< + CloudFormationDetectStackDriftParams, + CloudFormationDetectStackDriftResponse +> = { + id: 'cloudformation_detect_stack_drift', + name: 'CloudFormation Detect Stack Drift', + description: 'Initiate drift detection on a CloudFormation stack', + version: '1.0', + + params: { + awsRegion: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'AWS region (e.g., us-east-1)', + }, + awsAccessKeyId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'AWS access key ID', + }, + awsSecretAccessKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'AWS secret access key', + }, + stackName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Stack name or ID to detect drift on', + }, + }, + + request: { + url: '/api/tools/cloudformation/detect-stack-drift', + method: 'POST', + headers: () => ({ + 'Content-Type': 'application/json', + }), + body: (params) => ({ + region: params.awsRegion, + accessKeyId: params.awsAccessKeyId, + secretAccessKey: params.awsSecretAccessKey, + stackName: params.stackName, + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + if (!response.ok) { + throw new Error(data.error || 'Failed to detect CloudFormation stack drift') + } + + return { + success: true, + output: { + stackDriftDetectionId: data.output.stackDriftDetectionId, + }, + } + }, + + outputs: { + stackDriftDetectionId: { + type: 'string', + description: 'ID to use with Describe Stack Drift Detection Status to check results', + }, + }, +} diff --git a/apps/sim/tools/cloudformation/get_template.ts b/apps/sim/tools/cloudformation/get_template.ts new file mode 100644 index 00000000000..c3879a43fa9 --- /dev/null +++ b/apps/sim/tools/cloudformation/get_template.ts @@ -0,0 +1,77 @@ +import type { + CloudFormationGetTemplateParams, + CloudFormationGetTemplateResponse, +} from '@/tools/cloudformation/types' +import type { ToolConfig } from '@/tools/types' + +export const getTemplateTool: ToolConfig< + CloudFormationGetTemplateParams, + CloudFormationGetTemplateResponse +> = { + id: 'cloudformation_get_template', + name: 'CloudFormation Get Template', + description: 'Retrieve the template body for a CloudFormation stack', + version: '1.0', + + params: { + awsRegion: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'AWS region (e.g., us-east-1)', + }, + awsAccessKeyId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'AWS access key ID', + }, + awsSecretAccessKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'AWS secret access key', + }, + stackName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Stack name or ID', + }, + }, + + request: { + url: '/api/tools/cloudformation/get-template', + method: 'POST', + headers: () => ({ + 'Content-Type': 'application/json', + }), + body: (params) => ({ + region: params.awsRegion, + accessKeyId: params.awsAccessKeyId, + secretAccessKey: params.awsSecretAccessKey, + stackName: params.stackName, + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + if (!response.ok) { + throw new Error(data.error || 'Failed to get CloudFormation template') + } + + return { + success: true, + output: { + templateBody: data.output.templateBody, + stagesAvailable: data.output.stagesAvailable, + }, + } + }, + + outputs: { + templateBody: { type: 'string', description: 'The template body as a JSON or YAML string' }, + stagesAvailable: { type: 'array', description: 'Available template stages' }, + }, +} diff --git a/apps/sim/tools/cloudformation/index.ts b/apps/sim/tools/cloudformation/index.ts new file mode 100644 index 00000000000..8813b76df30 --- /dev/null +++ b/apps/sim/tools/cloudformation/index.ts @@ -0,0 +1,16 @@ +import { describeStackDriftDetectionStatusTool } from '@/tools/cloudformation/describe_stack_drift_detection_status' +import { describeStackEventsTool } from '@/tools/cloudformation/describe_stack_events' +import { describeStacksTool } from '@/tools/cloudformation/describe_stacks' +import { detectStackDriftTool } from '@/tools/cloudformation/detect_stack_drift' +import { getTemplateTool } from '@/tools/cloudformation/get_template' +import { listStackResourcesTool } from '@/tools/cloudformation/list_stack_resources' +import { validateTemplateTool } from '@/tools/cloudformation/validate_template' + +export const cloudformationDescribeStacksTool = describeStacksTool +export const cloudformationListStackResourcesTool = listStackResourcesTool +export const cloudformationDetectStackDriftTool = detectStackDriftTool +export const cloudformationDescribeStackDriftDetectionStatusTool = + describeStackDriftDetectionStatusTool +export const cloudformationDescribeStackEventsTool = describeStackEventsTool +export const cloudformationGetTemplateTool = getTemplateTool +export const cloudformationValidateTemplateTool = validateTemplateTool diff --git a/apps/sim/tools/cloudformation/list_stack_resources.ts b/apps/sim/tools/cloudformation/list_stack_resources.ts new file mode 100644 index 00000000000..c9488a3e791 --- /dev/null +++ b/apps/sim/tools/cloudformation/list_stack_resources.ts @@ -0,0 +1,78 @@ +import type { + CloudFormationListStackResourcesParams, + CloudFormationListStackResourcesResponse, +} from '@/tools/cloudformation/types' +import type { ToolConfig } from '@/tools/types' + +export const listStackResourcesTool: ToolConfig< + CloudFormationListStackResourcesParams, + CloudFormationListStackResourcesResponse +> = { + id: 'cloudformation_list_stack_resources', + name: 'CloudFormation List Stack Resources', + description: 'List all resources in a CloudFormation stack', + version: '1.0', + + params: { + awsRegion: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'AWS region (e.g., us-east-1)', + }, + awsAccessKeyId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'AWS access key ID', + }, + awsSecretAccessKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'AWS secret access key', + }, + stackName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Stack name or ID', + }, + }, + + request: { + url: '/api/tools/cloudformation/list-stack-resources', + method: 'POST', + headers: () => ({ + 'Content-Type': 'application/json', + }), + body: (params) => ({ + region: params.awsRegion, + accessKeyId: params.awsAccessKeyId, + secretAccessKey: params.awsSecretAccessKey, + stackName: params.stackName, + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + if (!response.ok) { + throw new Error(data.error || 'Failed to list CloudFormation stack resources') + } + + return { + success: true, + output: { + resources: data.output.resources, + }, + } + }, + + outputs: { + resources: { + type: 'array', + description: 'List of stack resources with type, status, and drift information', + }, + }, +} diff --git a/apps/sim/tools/cloudformation/types.ts b/apps/sim/tools/cloudformation/types.ts new file mode 100644 index 00000000000..e2d4cd91056 --- /dev/null +++ b/apps/sim/tools/cloudformation/types.ts @@ -0,0 +1,131 @@ +import type { ToolResponse } from '@/tools/types' + +export interface CloudFormationConnectionConfig { + awsRegion: string + awsAccessKeyId: string + awsSecretAccessKey: string +} + +export interface CloudFormationDescribeStacksParams extends CloudFormationConnectionConfig { + stackName?: string +} + +export interface CloudFormationListStackResourcesParams extends CloudFormationConnectionConfig { + stackName: string +} + +export interface CloudFormationDetectStackDriftParams extends CloudFormationConnectionConfig { + stackName: string +} + +export interface CloudFormationDescribeStackDriftDetectionStatusParams + extends CloudFormationConnectionConfig { + stackDriftDetectionId: string +} + +export interface CloudFormationDescribeStackEventsParams extends CloudFormationConnectionConfig { + stackName: string + limit?: number +} + +export interface CloudFormationGetTemplateParams extends CloudFormationConnectionConfig { + stackName: string +} + +export interface CloudFormationValidateTemplateParams extends CloudFormationConnectionConfig { + templateBody: string +} + +export interface CloudFormationDescribeStacksResponse extends ToolResponse { + output: { + stacks: { + stackName: string + stackId: string + stackStatus: string + stackStatusReason: string | undefined + creationTime: number | undefined + lastUpdatedTime: number | undefined + description: string | undefined + enableTerminationProtection: boolean | undefined + driftInformation: { + stackDriftStatus: string | undefined + lastCheckTimestamp: number | undefined + } | null + outputs: { outputKey: string; outputValue: string; description: string | undefined }[] + tags: { key: string; value: string }[] + }[] + } +} + +export interface CloudFormationListStackResourcesResponse extends ToolResponse { + output: { + resources: { + logicalResourceId: string + physicalResourceId: string | undefined + resourceType: string + resourceStatus: string + resourceStatusReason: string | undefined + lastUpdatedTimestamp: number | undefined + driftInformation: { + stackResourceDriftStatus: string | undefined + lastCheckTimestamp: number | undefined + } | null + }[] + } +} + +export interface CloudFormationDetectStackDriftResponse extends ToolResponse { + output: { + stackDriftDetectionId: string + } +} + +export interface CloudFormationDescribeStackDriftDetectionStatusResponse extends ToolResponse { + output: { + stackId: string + stackDriftDetectionId: string + stackDriftStatus: string | undefined + detectionStatus: string + detectionStatusReason: string | undefined + driftedStackResourceCount: number | undefined + timestamp: number | undefined + } +} + +export interface CloudFormationDescribeStackEventsResponse extends ToolResponse { + output: { + events: { + stackId: string + eventId: string + stackName: string + logicalResourceId: string | undefined + physicalResourceId: string | undefined + resourceType: string | undefined + resourceStatus: string | undefined + resourceStatusReason: string | undefined + timestamp: number | undefined + }[] + } +} + +export interface CloudFormationGetTemplateResponse extends ToolResponse { + output: { + templateBody: string + stagesAvailable: string[] + } +} + +export interface CloudFormationValidateTemplateResponse extends ToolResponse { + output: { + description: string | undefined + parameters: { + parameterKey: string | undefined + defaultValue: string | undefined + noEcho: boolean | undefined + description: string | undefined + }[] + capabilities: string[] + capabilitiesReason: string | undefined + declaredTransforms: string[] + } +} diff --git a/apps/sim/tools/cloudformation/validate_template.ts b/apps/sim/tools/cloudformation/validate_template.ts new file mode 100644 index 00000000000..90f067095d1 --- /dev/null +++ b/apps/sim/tools/cloudformation/validate_template.ts @@ -0,0 +1,89 @@ +import type { + CloudFormationValidateTemplateParams, + CloudFormationValidateTemplateResponse, +} from '@/tools/cloudformation/types' +import type { ToolConfig } from '@/tools/types' + +export const validateTemplateTool: ToolConfig< + CloudFormationValidateTemplateParams, + CloudFormationValidateTemplateResponse +> = { + id: 'cloudformation_validate_template', + name: 'CloudFormation Validate Template', + description: 'Validate a CloudFormation template for syntax and structural correctness', + version: '1.0', + + params: { + awsRegion: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'AWS region (e.g., us-east-1)', + }, + awsAccessKeyId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'AWS access key ID', + }, + awsSecretAccessKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'AWS secret access key', + }, + templateBody: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The CloudFormation template body (JSON or YAML)', + }, + }, + + request: { + url: '/api/tools/cloudformation/validate-template', + method: 'POST', + headers: () => ({ + 'Content-Type': 'application/json', + }), + body: (params) => ({ + region: params.awsRegion, + accessKeyId: params.awsAccessKeyId, + secretAccessKey: params.awsSecretAccessKey, + templateBody: params.templateBody, + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + if (!response.ok) { + throw new Error(data.error || 'Failed to validate CloudFormation template') + } + + return { + success: true, + output: { + description: data.output.description, + parameters: data.output.parameters, + capabilities: data.output.capabilities, + capabilitiesReason: data.output.capabilitiesReason, + declaredTransforms: data.output.declaredTransforms, + }, + } + }, + + outputs: { + description: { type: 'string', description: 'Template description' }, + parameters: { + type: 'array', + description: 'Template parameters with defaults and descriptions', + }, + capabilities: { type: 'array', description: 'Required capabilities (e.g., CAPABILITY_IAM)' }, + capabilitiesReason: { type: 'string', description: 'Reason capabilities are required' }, + declaredTransforms: { + type: 'array', + description: 'Transforms used in the template (e.g., AWS::Serverless-2016-10-31)', + }, + }, +} diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index 3dfbabf2a19..d5065039edd 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -275,6 +275,15 @@ import { cloudflareUpdateDnsRecordTool, cloudflareUpdateZoneSettingTool, } from '@/tools/cloudflare' +import { + cloudformationDescribeStackDriftDetectionStatusTool, + cloudformationDescribeStackEventsTool, + cloudformationDescribeStacksTool, + cloudformationDetectStackDriftTool, + cloudformationGetTemplateTool, + cloudformationListStackResourcesTool, + cloudformationValidateTemplateTool, +} from '@/tools/cloudformation' import { cloudwatchDescribeAlarmsTool, cloudwatchDescribeLogGroupsTool, @@ -3385,6 +3394,14 @@ export const tools: Record = { rds_delete: rdsDeleteTool, rds_execute: rdsExecuteTool, rds_introspect: rdsIntrospectTool, + cloudformation_describe_stacks: cloudformationDescribeStacksTool, + cloudformation_list_stack_resources: cloudformationListStackResourcesTool, + cloudformation_detect_stack_drift: cloudformationDetectStackDriftTool, + cloudformation_describe_stack_drift_detection_status: + cloudformationDescribeStackDriftDetectionStatusTool, + cloudformation_describe_stack_events: cloudformationDescribeStackEventsTool, + cloudformation_get_template: cloudformationGetTemplateTool, + cloudformation_validate_template: cloudformationValidateTemplateTool, cloudwatch_query_logs: cloudwatchQueryLogsTool, cloudwatch_describe_log_groups: cloudwatchDescribeLogGroupsTool, cloudwatch_describe_alarms: cloudwatchDescribeAlarmsTool, diff --git a/bun.lock b/bun.lock index ca0e8d8bb36..2813130991f 100644 --- a/bun.lock +++ b/bun.lock @@ -57,6 +57,7 @@ "@a2a-js/sdk": "0.3.7", "@anthropic-ai/sdk": "0.71.2", "@aws-sdk/client-bedrock-runtime": "3.940.0", + "@aws-sdk/client-cloudformation": "3.1019.0", "@aws-sdk/client-cloudwatch": "3.940.0", "@aws-sdk/client-cloudwatch-logs": "3.940.0", "@aws-sdk/client-dynamodb": "3.940.0", @@ -416,6 +417,8 @@ "@aws-sdk/client-bedrock-runtime": ["@aws-sdk/client-bedrock-runtime@3.940.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.940.0", "@aws-sdk/credential-provider-node": "3.940.0", "@aws-sdk/eventstream-handler-node": "3.936.0", "@aws-sdk/middleware-eventstream": "3.936.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.936.0", "@aws-sdk/middleware-user-agent": "3.940.0", "@aws-sdk/middleware-websocket": "3.936.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/token-providers": "3.940.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.940.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.5", "@smithy/eventstream-serde-browser": "^4.2.5", "@smithy/eventstream-serde-config-resolver": "^4.3.5", "@smithy/eventstream-serde-node": "^4.2.5", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.12", "@smithy/middleware-retry": "^4.4.12", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.11", "@smithy/util-defaults-mode-node": "^4.2.14", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-stream": "^4.5.6", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Gs6UUQP1zt8vahOxJ3BADcb3B+2KldUNA3bKa+KdK58de7N7tLJFJfZuXhFGGtwyNPh1aw6phtdP6dauq3OLWA=="], + "@aws-sdk/client-cloudformation": ["@aws-sdk/client-cloudformation@3.1019.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.25", "@aws-sdk/credential-provider-node": "^3.972.27", "@aws-sdk/middleware-host-header": "^3.972.8", "@aws-sdk/middleware-logger": "^3.972.8", "@aws-sdk/middleware-recursion-detection": "^3.972.9", "@aws-sdk/middleware-user-agent": "^3.972.26", "@aws-sdk/region-config-resolver": "^3.972.10", "@aws-sdk/types": "^3.973.6", "@aws-sdk/util-endpoints": "^3.996.5", "@aws-sdk/util-user-agent-browser": "^3.972.8", "@aws-sdk/util-user-agent-node": "^3.973.12", "@smithy/config-resolver": "^4.4.13", "@smithy/core": "^3.23.12", "@smithy/fetch-http-handler": "^5.3.15", "@smithy/hash-node": "^4.2.12", "@smithy/invalid-dependency": "^4.2.12", "@smithy/middleware-content-length": "^4.2.12", "@smithy/middleware-endpoint": "^4.4.27", "@smithy/middleware-retry": "^4.4.44", "@smithy/middleware-serde": "^4.2.15", "@smithy/middleware-stack": "^4.2.12", "@smithy/node-config-provider": "^4.3.12", "@smithy/node-http-handler": "^4.5.0", "@smithy/protocol-http": "^5.3.12", "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "@smithy/url-parser": "^4.2.12", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", "@smithy/util-defaults-mode-browser": "^4.3.43", "@smithy/util-defaults-mode-node": "^4.2.47", "@smithy/util-endpoints": "^3.3.3", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.12", "@smithy/util-utf8": "^4.2.2", "@smithy/util-waiter": "^4.2.13", "tslib": "^2.6.2" } }, "sha512-RNBtkQQ5IUqTdxaAe7ADwlJ/1qqW5kONLD1Mxr7PUWteEQwYR9ZJYscDul2qNkCWhu/vMKhk+qwJKPkdu2TNzA=="], + "@aws-sdk/client-cloudwatch": ["@aws-sdk/client-cloudwatch@3.940.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.940.0", "@aws-sdk/credential-provider-node": "3.940.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.936.0", "@aws-sdk/middleware-user-agent": "3.940.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.940.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.5", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-compression": "^4.3.12", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.12", "@smithy/middleware-retry": "^4.4.12", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.11", "@smithy/util-defaults-mode-node": "^4.2.14", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "@smithy/util-waiter": "^4.2.5", "tslib": "^2.6.2" } }, "sha512-C35xpPntRAGdEg3X5iKpSUCBaP3yxYNo1U95qipN/X1e0/TYIDWHwGt8Z1ntRafK19jp5oVzhRQ+PD1JAPSEzA=="], "@aws-sdk/client-cloudwatch-logs": ["@aws-sdk/client-cloudwatch-logs@3.940.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.940.0", "@aws-sdk/credential-provider-node": "3.940.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.936.0", "@aws-sdk/middleware-user-agent": "3.940.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.940.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.5", "@smithy/eventstream-serde-browser": "^4.2.5", "@smithy/eventstream-serde-config-resolver": "^4.3.5", "@smithy/eventstream-serde-node": "^4.2.5", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.12", "@smithy/middleware-retry": "^4.4.12", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.11", "@smithy/util-defaults-mode-node": "^4.2.14", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7dEIO3D98IxA9IhqixPJbzQsBkk4TchHHpFdd0JOhlSlihWhiwbf3ijUePJVXYJxcpRRtMmAMtDRLDzCSO+ZHg=="], @@ -3852,6 +3855,46 @@ "@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], + "@aws-sdk/client-cloudformation/@aws-sdk/core": ["@aws-sdk/core@3.973.26", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@aws-sdk/xml-builder": "^3.972.16", "@smithy/core": "^3.23.13", "@smithy/node-config-provider": "^4.3.12", "@smithy/property-provider": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/signature-v4": "^5.3.12", "@smithy/smithy-client": "^4.12.8", "@smithy/types": "^4.13.1", "@smithy/util-base64": "^4.3.2", "@smithy/util-middleware": "^4.2.12", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-A/E6n2W42ruU+sfWk+mMUOyVXbsSgGrY3MJ9/0Az5qUdG67y8I6HYzzoAa+e/lzxxl1uCYmEL6BTMi9ZiZnplQ=="], + + "@aws-sdk/client-cloudformation/@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.29", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.24", "@aws-sdk/credential-provider-http": "^3.972.26", "@aws-sdk/credential-provider-ini": "^3.972.28", "@aws-sdk/credential-provider-process": "^3.972.24", "@aws-sdk/credential-provider-sso": "^3.972.28", "@aws-sdk/credential-provider-web-identity": "^3.972.28", "@aws-sdk/types": "^3.973.6", "@smithy/credential-provider-imds": "^4.2.12", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-clSzDcvndpFJAggLDnDb36sPdlZYyEs5Zm6zgZjjUhwsJgSWiWKwFIXUVBcbruidNyBdbpOv2tNDL9sX8y3/0g=="], + + "@aws-sdk/client-cloudformation/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.972.8", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-wAr2REfKsqoKQ+OkNqvOShnBoh+nkPurDKW7uAeVSu6kUECnWlSJiPvnoqxGlfousEY/v9LfS9sNc46hjSYDIQ=="], + + "@aws-sdk/client-cloudformation/@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.972.8", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA=="], + + "@aws-sdk/client-cloudformation/@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.972.9", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-/Wt5+CT8dpTFQxEJ9iGy/UGrXr7p2wlIOEHvIr/YcHYByzoLjrqkYqXdJjd9UIgWjv7eqV2HnFJen93UTuwfTQ=="], + + "@aws-sdk/client-cloudformation/@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.972.28", "", { "dependencies": { "@aws-sdk/core": "^3.973.26", "@aws-sdk/types": "^3.973.6", "@aws-sdk/util-endpoints": "^3.996.5", "@smithy/core": "^3.23.13", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-retry": "^4.2.13", "tslib": "^2.6.2" } }, "sha512-cfWZFlVh7Va9lRay4PN2A9ARFzaBYcA097InT5M2CdRS05ECF5yaz86jET8Wsl2WcyKYEvVr/QNmKtYtafUHtQ=="], + + "@aws-sdk/client-cloudformation/@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.972.10", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@smithy/config-resolver": "^4.4.13", "@smithy/node-config-provider": "^4.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-1dq9ToC6e070QvnVhhbAs3bb5r6cQ10gTVc6cyRV5uvQe7P138TV2uG2i6+Yok4bAkVAcx5AqkTEBUvWEtBlsQ=="], + + "@aws-sdk/client-cloudformation/@aws-sdk/types": ["@aws-sdk/types@3.973.6", "", { "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw=="], + + "@aws-sdk/client-cloudformation/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.996.5", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@smithy/types": "^4.13.1", "@smithy/url-parser": "^4.2.12", "@smithy/util-endpoints": "^3.3.3", "tslib": "^2.6.2" } }, "sha512-Uh93L5sXFNbyR5sEPMzUU8tJ++Ku97EY4udmC01nB8Zu+xfBPwpIwJ6F7snqQeq8h2pf+8SGN5/NoytfKgYPIw=="], + + "@aws-sdk/client-cloudformation/@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.972.8", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@smithy/types": "^4.13.1", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA=="], + + "@aws-sdk/client-cloudformation/@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.973.14", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "^3.972.28", "@aws-sdk/types": "^3.973.6", "@smithy/node-config-provider": "^4.3.12", "@smithy/types": "^4.13.1", "@smithy/util-config-provider": "^4.2.2", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-vNSB/DYaPOyujVZBg/zUznH9QC142MaTHVmaFlF7uzzfg3CgT9f/l4C0Yi+vU/tbBhxVcXVB90Oohk5+o+ZbWw=="], + + "@aws-sdk/client-cloudformation/@smithy/core": ["@smithy/core@3.23.13", "", { "dependencies": { "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/url-parser": "^4.2.12", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-middleware": "^4.2.12", "@smithy/util-stream": "^4.5.21", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-J+2TT9D6oGsUVXVEMvz8h2EmdVnkBiy2auCie4aSJMvKlzUtO5hqjEzXhoCUkIMo7gAYjbQcN0g/MMSXEhDs1Q=="], + + "@aws-sdk/client-cloudformation/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.28", "", { "dependencies": { "@smithy/core": "^3.23.13", "@smithy/middleware-serde": "^4.2.16", "@smithy/node-config-provider": "^4.3.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "@smithy/url-parser": "^4.2.12", "@smithy/util-middleware": "^4.2.12", "tslib": "^2.6.2" } }, "sha512-p1gfYpi91CHcs5cBq982UlGlDrxoYUX6XdHSo91cQ2KFuz6QloHosO7Jc60pJiVmkWrKOV8kFYlGFFbQ2WUKKQ=="], + + "@aws-sdk/client-cloudformation/@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.46", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.12", "@smithy/protocol-http": "^5.3.12", "@smithy/service-error-classification": "^4.2.12", "@smithy/smithy-client": "^4.12.8", "@smithy/types": "^4.13.1", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.13", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-SpvWNNOPOrKQGUqZbEPO+es+FRXMWvIyzUKUOYdDgdlA6BdZj/R58p4umoQ76c2oJC44PiM7mKizyyex1IJzow=="], + + "@aws-sdk/client-cloudformation/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.16", "", { "dependencies": { "@smithy/core": "^3.23.13", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-beqfV+RZ9RSv+sQqor3xroUUYgRFCGRw6niGstPG8zO9LgTl0B0MCucxjmrH/2WwksQN7UUgI7KNANoZv+KALA=="], + + "@aws-sdk/client-cloudformation/@smithy/node-http-handler": ["@smithy/node-http-handler@4.5.1", "", { "dependencies": { "@smithy/protocol-http": "^5.3.12", "@smithy/querystring-builder": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-ejjxdAXjkPIs9lyYyVutOGNOraqUE9v/NjGMKwwFrfOM354wfSD8lmlj8hVwUzQmlLLF4+udhfCX9Exnbmvfzw=="], + + "@aws-sdk/client-cloudformation/@smithy/smithy-client": ["@smithy/smithy-client@4.12.8", "", { "dependencies": { "@smithy/core": "^3.23.13", "@smithy/middleware-endpoint": "^4.4.28", "@smithy/middleware-stack": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-stream": "^4.5.21", "tslib": "^2.6.2" } }, "sha512-aJaAX7vHe5i66smoSSID7t4rKY08PbD8EBU7DOloixvhOozfYWdcSYE4l6/tjkZ0vBZhGjheWzB2mh31sLgCMA=="], + + "@aws-sdk/client-cloudformation/@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.44", "", { "dependencies": { "@smithy/property-provider": "^4.2.12", "@smithy/smithy-client": "^4.12.8", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-eZg6XzaCbVr2S5cAErU5eGBDaOVTuTo1I65i4tQcHENRcZ8rMWhQy1DaIYUSLyZjsfXvmCqZrstSMYyGFocvHA=="], + + "@aws-sdk/client-cloudformation/@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.48", "", { "dependencies": { "@smithy/config-resolver": "^4.4.13", "@smithy/credential-provider-imds": "^4.2.12", "@smithy/node-config-provider": "^4.3.12", "@smithy/property-provider": "^4.2.12", "@smithy/smithy-client": "^4.12.8", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-FqOKTlqSaoV3nzO55pMs5NBnZX8EhoI0DGmn9kbYeXWppgHD6dchyuj2HLqp4INJDJbSrj6OFYJkAh/WhSzZPg=="], + + "@aws-sdk/client-cloudformation/@smithy/util-retry": ["@smithy/util-retry@4.2.13", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-qQQsIvL0MGIbUjeSrg0/VlQ3jGNKyM3/2iU3FPNgy01z+Sp4OvcaxbgIoFOTvB61ZoohtutuOvOcgmhbD0katQ=="], + "@aws-sdk/client-s3/@aws-sdk/core": ["@aws-sdk/core@3.973.24", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@aws-sdk/xml-builder": "^3.972.15", "@smithy/core": "^3.23.12", "@smithy/node-config-provider": "^4.3.12", "@smithy/property-provider": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/signature-v4": "^5.3.12", "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "@smithy/util-base64": "^4.3.2", "@smithy/util-middleware": "^4.2.12", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-vvf82RYQu2GidWAuQq+uIzaPz9V0gSCXVqdVzRosgl5rXcspXOpSD3wFreGGW6AYymPr97Z69kjVnLePBxloDw=="], "@aws-sdk/client-s3/@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.25", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.22", "@aws-sdk/credential-provider-http": "^3.972.24", "@aws-sdk/credential-provider-ini": "^3.972.24", "@aws-sdk/credential-provider-process": "^3.972.22", "@aws-sdk/credential-provider-sso": "^3.972.24", "@aws-sdk/credential-provider-web-identity": "^3.972.24", "@aws-sdk/types": "^3.973.6", "@smithy/credential-provider-imds": "^4.2.12", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-m7dR0Dsva2P+VUpL+VkC0WwiDby5pgmWXkRVDB5rlwv0jXJrQJf7YMtCoM8Wjk0H9jPeCYOxOXXcIgp/qp5Alg=="], @@ -4534,6 +4577,24 @@ "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], + "@aws-sdk/client-cloudformation/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.16", "", { "dependencies": { "@smithy/types": "^4.13.1", "fast-xml-parser": "5.5.8", "tslib": "^2.6.2" } }, "sha512-iu2pyvaqmeatIJLURLqx9D+4jKAdTH20ntzB6BFwjyN7V960r4jK32mx0Zf7YbtOYAbmbtQfDNuL60ONinyw7A=="], + + "@aws-sdk/client-cloudformation/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.24", "", { "dependencies": { "@aws-sdk/core": "^3.973.26", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-FWg8uFmT6vQM7VuzELzwVo5bzExGaKHdubn0StjgrcU5FvuLExUe+k06kn/40uKv59rYzhez8eFNM4yYE/Yb/w=="], + + "@aws-sdk/client-cloudformation/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.26", "", { "dependencies": { "@aws-sdk/core": "^3.973.26", "@aws-sdk/types": "^3.973.6", "@smithy/fetch-http-handler": "^5.3.15", "@smithy/node-http-handler": "^4.5.1", "@smithy/property-provider": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/smithy-client": "^4.12.8", "@smithy/types": "^4.13.1", "@smithy/util-stream": "^4.5.21", "tslib": "^2.6.2" } }, "sha512-CY4ppZ+qHYqcXqBVi//sdHST1QK3KzOEiLtpLsc9W2k2vfZPKExGaQIsOwcyvjpjUEolotitmd3mUNY56IwDEA=="], + + "@aws-sdk/client-cloudformation/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.972.28", "", { "dependencies": { "@aws-sdk/core": "^3.973.26", "@aws-sdk/credential-provider-env": "^3.972.24", "@aws-sdk/credential-provider-http": "^3.972.26", "@aws-sdk/credential-provider-login": "^3.972.28", "@aws-sdk/credential-provider-process": "^3.972.24", "@aws-sdk/credential-provider-sso": "^3.972.28", "@aws-sdk/credential-provider-web-identity": "^3.972.28", "@aws-sdk/nested-clients": "^3.996.18", "@aws-sdk/types": "^3.973.6", "@smithy/credential-provider-imds": "^4.2.12", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-wXYvq3+uQcZV7k+bE4yDXCTBdzWTU9x/nMiKBfzInmv6yYK1veMK0AKvRfRBd72nGWYKcL6AxwiPg9z/pYlgpw=="], + + "@aws-sdk/client-cloudformation/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.24", "", { "dependencies": { "@aws-sdk/core": "^3.973.26", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Q2k/XLrFXhEztPHqj4SLCNID3hEPdlhh1CDLBpNnM+1L8fq7P+yON9/9M1IGN/dA5W45v44ylERfXtDAlmMNmw=="], + + "@aws-sdk/client-cloudformation/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.972.28", "", { "dependencies": { "@aws-sdk/core": "^3.973.26", "@aws-sdk/nested-clients": "^3.996.18", "@aws-sdk/token-providers": "3.1021.0", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-IoUlmKMLEITFn1SiCTjPfR6KrE799FBo5baWyk/5Ppar2yXZoUdaRqZzJzK6TcJxx450M8m8DbpddRVYlp5R/A=="], + + "@aws-sdk/client-cloudformation/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.28", "", { "dependencies": { "@aws-sdk/core": "^3.973.26", "@aws-sdk/nested-clients": "^3.996.18", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-d+6h0SD8GGERzKe27v5rOzNGKOl0D+l0bWJdqrxH8WSQzHzjsQFIAPgIeOTUwBHVsKKwtSxc91K/SWax6XgswQ=="], + + "@aws-sdk/client-cloudformation/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.21", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.15", "@smithy/node-http-handler": "^4.5.1", "@smithy/types": "^4.13.1", "@smithy/util-base64": "^4.3.2", "@smithy/util-buffer-from": "^4.2.2", "@smithy/util-hex-encoding": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-KzSg+7KKywLnkoKejRtIBXDmwBfjGvg1U1i/etkC7XSWUyFCoLno1IohV2c74IzQqdhX5y3uE44r/8/wuK+A7Q=="], + + "@aws-sdk/client-cloudformation/@smithy/smithy-client/@smithy/util-stream": ["@smithy/util-stream@4.5.21", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.15", "@smithy/node-http-handler": "^4.5.1", "@smithy/types": "^4.13.1", "@smithy/util-base64": "^4.3.2", "@smithy/util-buffer-from": "^4.2.2", "@smithy/util-hex-encoding": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-KzSg+7KKywLnkoKejRtIBXDmwBfjGvg1U1i/etkC7XSWUyFCoLno1IohV2c74IzQqdhX5y3uE44r/8/wuK+A7Q=="], + "@aws-sdk/client-s3/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.15", "", { "dependencies": { "@smithy/types": "^4.13.1", "fast-xml-parser": "5.5.8", "tslib": "^2.6.2" } }, "sha512-PxMRlCFNiQnke9YR29vjFQwz4jq+6Q04rOVFeTDR2K7Qpv9h9FOWOxG+zJjageimYbWqE3bTuLjmryWHAWbvaA=="], "@aws-sdk/client-s3/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.22", "", { "dependencies": { "@aws-sdk/core": "^3.973.24", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-cXp0VTDWT76p3hyK5D51yIKEfpf6/zsUvMfaB8CkyqadJxMQ8SbEeVroregmDlZbtG31wkj9ei0WnftmieggLg=="], @@ -5092,6 +5153,20 @@ "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], + "@aws-sdk/client-cloudformation/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.5.8", "", { "dependencies": { "fast-xml-builder": "^1.1.4", "path-expression-matcher": "^1.2.0", "strnum": "^2.2.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ=="], + + "@aws-sdk/client-cloudformation/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream": ["@smithy/util-stream@4.5.21", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.15", "@smithy/node-http-handler": "^4.5.1", "@smithy/types": "^4.13.1", "@smithy/util-base64": "^4.3.2", "@smithy/util-buffer-from": "^4.2.2", "@smithy/util-hex-encoding": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-KzSg+7KKywLnkoKejRtIBXDmwBfjGvg1U1i/etkC7XSWUyFCoLno1IohV2c74IzQqdhX5y3uE44r/8/wuK+A7Q=="], + + "@aws-sdk/client-cloudformation/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.28", "", { "dependencies": { "@aws-sdk/core": "^3.973.26", "@aws-sdk/nested-clients": "^3.996.18", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-ZSTfO6jqUTCysbdBPtEX5OUR//3rbD0lN7jO3sQeS2Gjr/Y+DT6SbIJ0oT2cemNw3UzKu97sNONd1CwNMthuZQ=="], + + "@aws-sdk/client-cloudformation/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.996.18", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.26", "@aws-sdk/middleware-host-header": "^3.972.8", "@aws-sdk/middleware-logger": "^3.972.8", "@aws-sdk/middleware-recursion-detection": "^3.972.9", "@aws-sdk/middleware-user-agent": "^3.972.28", "@aws-sdk/region-config-resolver": "^3.972.10", "@aws-sdk/types": "^3.973.6", "@aws-sdk/util-endpoints": "^3.996.5", "@aws-sdk/util-user-agent-browser": "^3.972.8", "@aws-sdk/util-user-agent-node": "^3.973.14", "@smithy/config-resolver": "^4.4.13", "@smithy/core": "^3.23.13", "@smithy/fetch-http-handler": "^5.3.15", "@smithy/hash-node": "^4.2.12", "@smithy/invalid-dependency": "^4.2.12", "@smithy/middleware-content-length": "^4.2.12", "@smithy/middleware-endpoint": "^4.4.28", "@smithy/middleware-retry": "^4.4.46", "@smithy/middleware-serde": "^4.2.16", "@smithy/middleware-stack": "^4.2.12", "@smithy/node-config-provider": "^4.3.12", "@smithy/node-http-handler": "^4.5.1", "@smithy/protocol-http": "^5.3.12", "@smithy/smithy-client": "^4.12.8", "@smithy/types": "^4.13.1", "@smithy/url-parser": "^4.2.12", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", "@smithy/util-defaults-mode-browser": "^4.3.44", "@smithy/util-defaults-mode-node": "^4.2.48", "@smithy/util-endpoints": "^3.3.3", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.13", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-c7ZSIXrESxHKx2Mcopgd8AlzZgoXMr20fkx5ViPWPOLBvmyhw9VwJx/Govg8Ef/IhEon5R9l53Z8fdYSEmp6VA=="], + + "@aws-sdk/client-cloudformation/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.996.18", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.26", "@aws-sdk/middleware-host-header": "^3.972.8", "@aws-sdk/middleware-logger": "^3.972.8", "@aws-sdk/middleware-recursion-detection": "^3.972.9", "@aws-sdk/middleware-user-agent": "^3.972.28", "@aws-sdk/region-config-resolver": "^3.972.10", "@aws-sdk/types": "^3.973.6", "@aws-sdk/util-endpoints": "^3.996.5", "@aws-sdk/util-user-agent-browser": "^3.972.8", "@aws-sdk/util-user-agent-node": "^3.973.14", "@smithy/config-resolver": "^4.4.13", "@smithy/core": "^3.23.13", "@smithy/fetch-http-handler": "^5.3.15", "@smithy/hash-node": "^4.2.12", "@smithy/invalid-dependency": "^4.2.12", "@smithy/middleware-content-length": "^4.2.12", "@smithy/middleware-endpoint": "^4.4.28", "@smithy/middleware-retry": "^4.4.46", "@smithy/middleware-serde": "^4.2.16", "@smithy/middleware-stack": "^4.2.12", "@smithy/node-config-provider": "^4.3.12", "@smithy/node-http-handler": "^4.5.1", "@smithy/protocol-http": "^5.3.12", "@smithy/smithy-client": "^4.12.8", "@smithy/types": "^4.13.1", "@smithy/url-parser": "^4.2.12", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", "@smithy/util-defaults-mode-browser": "^4.3.44", "@smithy/util-defaults-mode-node": "^4.2.48", "@smithy/util-endpoints": "^3.3.3", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.13", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-c7ZSIXrESxHKx2Mcopgd8AlzZgoXMr20fkx5ViPWPOLBvmyhw9VwJx/Govg8Ef/IhEon5R9l53Z8fdYSEmp6VA=="], + + "@aws-sdk/client-cloudformation/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1021.0", "", { "dependencies": { "@aws-sdk/core": "^3.973.26", "@aws-sdk/nested-clients": "^3.996.18", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-TKY6h9spUk3OLs5v1oAgW9mAeBE3LAGNBwJokLy96wwmd4W2v/tYlXseProyed9ValDj2u1jK/4Rg1T+1NXyJA=="], + + "@aws-sdk/client-cloudformation/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.996.18", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.26", "@aws-sdk/middleware-host-header": "^3.972.8", "@aws-sdk/middleware-logger": "^3.972.8", "@aws-sdk/middleware-recursion-detection": "^3.972.9", "@aws-sdk/middleware-user-agent": "^3.972.28", "@aws-sdk/region-config-resolver": "^3.972.10", "@aws-sdk/types": "^3.973.6", "@aws-sdk/util-endpoints": "^3.996.5", "@aws-sdk/util-user-agent-browser": "^3.972.8", "@aws-sdk/util-user-agent-node": "^3.973.14", "@smithy/config-resolver": "^4.4.13", "@smithy/core": "^3.23.13", "@smithy/fetch-http-handler": "^5.3.15", "@smithy/hash-node": "^4.2.12", "@smithy/invalid-dependency": "^4.2.12", "@smithy/middleware-content-length": "^4.2.12", "@smithy/middleware-endpoint": "^4.4.28", "@smithy/middleware-retry": "^4.4.46", "@smithy/middleware-serde": "^4.2.16", "@smithy/middleware-stack": "^4.2.12", "@smithy/node-config-provider": "^4.3.12", "@smithy/node-http-handler": "^4.5.1", "@smithy/protocol-http": "^5.3.12", "@smithy/smithy-client": "^4.12.8", "@smithy/types": "^4.13.1", "@smithy/url-parser": "^4.2.12", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", "@smithy/util-defaults-mode-browser": "^4.3.44", "@smithy/util-defaults-mode-node": "^4.2.48", "@smithy/util-endpoints": "^3.3.3", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.13", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-c7ZSIXrESxHKx2Mcopgd8AlzZgoXMr20fkx5ViPWPOLBvmyhw9VwJx/Govg8Ef/IhEon5R9l53Z8fdYSEmp6VA=="], + "@aws-sdk/client-s3/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.5.8", "", { "dependencies": { "fast-xml-builder": "^1.1.4", "path-expression-matcher": "^1.2.0", "strnum": "^2.2.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ=="], "@aws-sdk/client-s3/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.24", "", { "dependencies": { "@aws-sdk/core": "^3.973.24", "@aws-sdk/nested-clients": "^3.996.14", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-sIk8oa6AzDoUhxsR11svZESqvzGuXesw62Rl2oW6wguZx8i9cdGCvkFg+h5K7iucUZP8wyWibUbJMc+J66cu5g=="],