diff --git a/apps/docs/components/icons.tsx b/apps/docs/components/icons.tsx index 8b0ed3aabb1..2806cd4d314 100644 --- a/apps/docs/components/icons.tsx +++ b/apps/docs/components/icons.tsx @@ -4687,6 +4687,33 @@ export function CloudFormationIcon(props: SVGProps) { ) } +export function AthenaIcon(props: SVGProps) { + return ( + + + + + + ) +} + export function CloudWatchIcon(props: SVGProps) { return ( = { arxiv: ArxivIcon, asana: AsanaIcon, ashby: AshbyIcon, + athena: AthenaIcon, attio: AttioIcon, box: BoxCompanyIcon, brandfetch: BrandfetchIcon, diff --git a/apps/docs/content/docs/en/tools/athena.mdx b/apps/docs/content/docs/en/tools/athena.mdx new file mode 100644 index 00000000000..d77394bcefc --- /dev/null +++ b/apps/docs/content/docs/en/tools/athena.mdx @@ -0,0 +1,238 @@ +--- +title: Athena +description: Run SQL queries on data in Amazon S3 using AWS Athena +--- + +import { BlockInfoCard } from "@/components/ui/block-info-card" + + + +{/* MANUAL-CONTENT-START:intro */} +[Amazon Athena](https://aws.amazon.com/athena/) is an interactive query service from AWS that makes it easy to analyze data directly in Amazon S3 using standard SQL. Athena is serverless, so there is no infrastructure to manage, and you pay only for the queries you run. + +With Athena, you can: + +- **Query data in S3**: Run SQL queries directly against data stored in Amazon S3 without loading it into a database +- **Support multiple formats**: Query CSV, JSON, Parquet, ORC, Avro, and other common data formats +- **Integrate with AWS Glue**: Use the AWS Glue Data Catalog to manage table metadata and schemas +- **Scale automatically**: Handle queries of any size without provisioning servers or clusters +- **Save and reuse queries**: Create named queries for frequently used SQL statements + +In Sim, the Athena integration enables your agents to run SQL queries against data in S3, check query execution status, retrieve results, and manage saved queries — all within your agent workflows. Supported operations include: + +- **Start Query**: Execute SQL queries against your S3 data +- **Get Query Execution**: Check the status and details of a running or completed query +- **Get Query Results**: Retrieve the results of a completed query +- **Stop Query**: Cancel a running query execution +- **List Query Executions**: View recent query execution IDs +- **Create Named Query**: Save a query for reuse +- **Get Named Query**: Retrieve details of a saved query +- **List Named Queries**: View all saved query IDs + +This integration empowers Sim agents to automate data analysis tasks using AWS Athena, enabling workflows that query, monitor, and manage large-scale data in S3 without manual effort or infrastructure management. +{/* MANUAL-CONTENT-END */} + + +## Usage Instructions + +Integrate AWS Athena into workflows. Execute SQL queries against data in S3, check query status, retrieve results, manage named queries, and list executions. Requires AWS access key and secret access key. + + + +## Tools + +### `athena_start_query` + +Start an SQL query execution in AWS Athena + +#### 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 | +| `queryString` | string | Yes | SQL query string to execute | +| `database` | string | No | Database name within the catalog | +| `catalog` | string | No | Data catalog name \(default: AwsDataCatalog\) | +| `outputLocation` | string | No | S3 output location for query results \(e.g., s3://bucket/path/\) | +| `workGroup` | string | No | Workgroup to execute the query in \(default: primary\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `queryExecutionId` | string | Unique ID of the started query execution | + +### `athena_get_query_execution` + +Get the status and details of an Athena query execution + +#### 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 | +| `queryExecutionId` | string | Yes | Query execution ID to check | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `queryExecutionId` | string | Query execution ID | +| `query` | string | SQL query string | +| `state` | string | Query state \(QUEUED, RUNNING, SUCCEEDED, FAILED, CANCELLED\) | +| `stateChangeReason` | string | Reason for state change \(e.g., error message\) | +| `statementType` | string | Statement type \(DDL, DML, UTILITY\) | +| `database` | string | Database name | +| `catalog` | string | Data catalog name | +| `workGroup` | string | Workgroup name | +| `submissionDateTime` | number | Query submission time \(Unix epoch ms\) | +| `completionDateTime` | number | Query completion time \(Unix epoch ms\) | +| `dataScannedInBytes` | number | Amount of data scanned in bytes | +| `engineExecutionTimeInMillis` | number | Engine execution time in milliseconds | +| `queryPlanningTimeInMillis` | number | Query planning time in milliseconds | +| `queryQueueTimeInMillis` | number | Time the query spent in queue in milliseconds | +| `totalExecutionTimeInMillis` | number | Total execution time in milliseconds | +| `outputLocation` | string | S3 location of query results | + +### `athena_get_query_results` + +Retrieve the results of a completed Athena query execution + +#### 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 | +| `queryExecutionId` | string | Yes | Query execution ID to get results for | +| `maxResults` | number | No | Maximum number of rows to return \(1-1000\) | +| `nextToken` | string | No | Pagination token from a previous request | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `columns` | array | Column metadata \(name and type\) | +| `rows` | array | Result rows as key-value objects | +| `nextToken` | string | Pagination token for next page of results | +| `updateCount` | number | Number of rows affected \(for INSERT/UPDATE statements\) | + +### `athena_stop_query` + +Stop a running Athena query execution + +#### 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 | +| `queryExecutionId` | string | Yes | Query execution ID to stop | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Whether the query was successfully stopped | + +### `athena_list_query_executions` + +List recent Athena query execution IDs + +#### 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 | +| `workGroup` | string | No | Workgroup to list executions for \(default: primary\) | +| `maxResults` | number | No | Maximum number of results \(0-50\) | +| `nextToken` | string | No | Pagination token from a previous request | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `queryExecutionIds` | array | List of query execution IDs | +| `nextToken` | string | Pagination token for next page | + +### `athena_create_named_query` + +Create a saved/named query in AWS Athena + +#### 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 | +| `name` | string | Yes | Name for the saved query | +| `database` | string | Yes | Database the query runs against | +| `queryString` | string | Yes | SQL query string to save | +| `description` | string | No | Description of the named query | +| `workGroup` | string | No | Workgroup to create the named query in | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `namedQueryId` | string | ID of the created named query | + +### `athena_get_named_query` + +Get details of a saved/named query in AWS Athena + +#### 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 | +| `namedQueryId` | string | Yes | Named query ID to retrieve | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `namedQueryId` | string | Named query ID | +| `name` | string | Name of the saved query | +| `description` | string | Query description | +| `database` | string | Database the query runs against | +| `queryString` | string | SQL query string | +| `workGroup` | string | Workgroup name | + +### `athena_list_named_queries` + +List saved/named query IDs in AWS Athena + +#### 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 | +| `workGroup` | string | No | Workgroup to list named queries for | +| `maxResults` | number | No | Maximum number of results \(0-50\) | +| `nextToken` | string | No | Pagination token from a previous request | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `namedQueryIds` | array | List of named query IDs | +| `nextToken` | string | Pagination token for next page | + + diff --git a/apps/docs/content/docs/en/tools/meta.json b/apps/docs/content/docs/en/tools/meta.json index e413b867f71..2a8e6ba0c89 100644 --- a/apps/docs/content/docs/en/tools/meta.json +++ b/apps/docs/content/docs/en/tools/meta.json @@ -13,6 +13,7 @@ "arxiv", "asana", "ashby", + "athena", "attio", "box", "brandfetch", diff --git a/apps/sim/app/(auth)/signup/signup-form.tsx b/apps/sim/app/(auth)/signup/signup-form.tsx index 55a0508ec1b..afb27cd729a 100644 --- a/apps/sim/app/(auth)/signup/signup-form.tsx +++ b/apps/sim/app/(auth)/signup/signup-form.tsx @@ -270,10 +270,8 @@ function SignupFormContent({ name: sanitizedName, }, { - fetchOptions: { - headers: { - ...(token ? { 'x-captcha-response': token } : {}), - }, + headers: { + ...(token ? { 'x-captcha-response': token } : {}), }, onError: (ctx) => { logger.error('Signup error:', ctx.error) @@ -282,10 +280,7 @@ function SignupFormContent({ let errorCode = 'unknown' if (ctx.error.code?.includes('USER_ALREADY_EXISTS')) { errorCode = 'user_already_exists' - errorMessage.push( - 'An account with this email already exists. Please sign in instead.' - ) - setEmailError(errorMessage[0]) + setEmailError('An account with this email already exists. Please sign in instead.') } else if ( ctx.error.code?.includes('BAD_REQUEST') || ctx.error.message?.includes('Email and password sign up is not enabled') diff --git a/apps/sim/app/(landing)/components/features/components/features-preview.tsx b/apps/sim/app/(landing)/components/features/components/features-preview.tsx index e9a69ae8f9f..e485396a7e6 100644 --- a/apps/sim/app/(landing)/components/features/components/features-preview.tsx +++ b/apps/sim/app/(landing)/components/features/components/features-preview.tsx @@ -18,6 +18,7 @@ import { xAIIcon, } from '@/components/icons' import { cn } from '@/lib/core/utils/cn' +import { workflowBorderColor } from '@/lib/workspaces/colors' interface FeaturesPreviewProps { activeTab: number @@ -383,7 +384,7 @@ function MiniCardIcon({ variant, color }: { variant: CardVariant; color?: string className='h-[7px] w-[7px] flex-shrink-0 rounded-[1.5px] border' style={{ backgroundColor: c, - borderColor: `${c}60`, + borderColor: workflowBorderColor(c), backgroundClip: 'padding-box', }} /> @@ -470,7 +471,7 @@ function WorkflowCardBody({ color }: { color: string }) { className='absolute top-2.5 left-[40px] h-[14px] w-[14px] rounded-[3px] border-[2px]' style={{ backgroundColor: color, - borderColor: `${color}60`, + borderColor: workflowBorderColor(color), backgroundClip: 'padding-box', }} /> @@ -481,7 +482,7 @@ function WorkflowCardBody({ color }: { color: string }) { className='absolute top-[36px] left-[68px] h-[14px] w-[14px] rounded-[3px] border-[2px]' style={{ backgroundColor: color, - borderColor: `${color}60`, + borderColor: workflowBorderColor(color), backgroundClip: 'padding-box', opacity: 0.5, }} @@ -896,7 +897,7 @@ function MockLogDetailsSidebar({ selectedRow, onPrev, onNext }: MockLogDetailsSi className='h-[10px] w-[10px] shrink-0 rounded-[3px] border-[1.5px]' style={{ backgroundColor: color, - borderColor: `${color}60`, + borderColor: workflowBorderColor(color), backgroundClip: 'padding-box', }} /> diff --git a/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-logs/landing-preview-logs.tsx b/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-logs/landing-preview-logs.tsx index 2070fab6468..6b691bfcd3f 100644 --- a/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-logs/landing-preview-logs.tsx +++ b/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-logs/landing-preview-logs.tsx @@ -5,6 +5,7 @@ import { Download } from 'lucide-react' import { ArrowUpDown, Badge, Library, ListFilter, Search } from '@/components/emcn' import type { BadgeProps } from '@/components/emcn/components/badge/badge' import { cn } from '@/lib/core/utils/cn' +import { workflowBorderColor } from '@/lib/workspaces/colors' interface LogRow { id: string @@ -283,7 +284,7 @@ export function LandingPreviewLogs() { className='h-[10px] w-[10px] flex-shrink-0 rounded-[3px] border-[1.5px]' style={{ backgroundColor: log.workflowColor, - borderColor: `${log.workflowColor}60`, + borderColor: workflowBorderColor(log.workflowColor), backgroundClip: 'padding-box', }} /> diff --git a/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-sidebar/landing-preview-sidebar.tsx b/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-sidebar/landing-preview-sidebar.tsx index 1b754f65f36..d7707aa0c8d 100644 --- a/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-sidebar/landing-preview-sidebar.tsx +++ b/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-sidebar/landing-preview-sidebar.tsx @@ -11,6 +11,7 @@ import { Table, } from '@/components/emcn/icons' import { cn } from '@/lib/core/utils/cn' +import { workflowBorderColor } from '@/lib/workspaces/colors' import type { PreviewWorkflow } from '@/app/(landing)/components/landing-preview/components/landing-preview-workflow/workflow-data' export type SidebarView = @@ -211,7 +212,7 @@ export function LandingPreviewSidebar({ className='h-[14px] w-[14px] flex-shrink-0 rounded-[4px] border-[2.5px]' style={{ backgroundColor: workflow.color, - borderColor: `${workflow.color}60`, + borderColor: workflowBorderColor(workflow.color), backgroundClip: 'padding-box', }} /> diff --git a/apps/sim/app/(landing)/integrations/data/icon-mapping.ts b/apps/sim/app/(landing)/integrations/data/icon-mapping.ts index 898ee4287ee..ddf28d4d7a6 100644 --- a/apps/sim/app/(landing)/integrations/data/icon-mapping.ts +++ b/apps/sim/app/(landing)/integrations/data/icon-mapping.ts @@ -16,6 +16,7 @@ import { ArxivIcon, AsanaIcon, AshbyIcon, + AthenaIcon, AttioIcon, AzureIcon, BoxCompanyIcon, @@ -205,6 +206,7 @@ export const blockTypeToIconMap: Record = { arxiv: ArxivIcon, asana: AsanaIcon, ashby: AshbyIcon, + athena: AthenaIcon, attio: AttioIcon, box: BoxCompanyIcon, brandfetch: BrandfetchIcon, diff --git a/apps/sim/app/(landing)/integrations/data/integrations.json b/apps/sim/app/(landing)/integrations/data/integrations.json index 7108dacc096..e6658415998 100644 --- a/apps/sim/app/(landing)/integrations/data/integrations.json +++ b/apps/sim/app/(landing)/integrations/data/integrations.json @@ -971,6 +971,57 @@ "integrationType": "hr", "tags": ["hiring"] }, + { + "type": "athena", + "slug": "athena", + "name": "Athena", + "description": "Run SQL queries on data in Amazon S3 using AWS Athena", + "longDescription": "Integrate AWS Athena into workflows. Execute SQL queries against data in S3, check query status, retrieve results, manage named queries, and list executions. Requires AWS access key and secret access key.", + "bgColor": "linear-gradient(45deg, #4D27A8 0%, #A166FF 100%)", + "iconName": "AthenaIcon", + "docsUrl": "https://docs.sim.ai/tools/athena", + "operations": [ + { + "name": "Start Query", + "description": "Start an SQL query execution in AWS Athena" + }, + { + "name": "Get Query Execution", + "description": "Get the status and details of an Athena query execution" + }, + { + "name": "Get Query Results", + "description": "Retrieve the results of a completed Athena query execution" + }, + { + "name": "Stop Query", + "description": "Stop a running Athena query execution" + }, + { + "name": "List Query Executions", + "description": "List recent Athena query execution IDs" + }, + { + "name": "Create Named Query", + "description": "Create a saved/named query in AWS Athena" + }, + { + "name": "Get Named Query", + "description": "Get details of a saved/named query in AWS Athena" + }, + { + "name": "List Named Queries", + "description": "List saved/named query IDs in AWS Athena" + } + ], + "operationCount": 8, + "triggers": [], + "triggerCount": 0, + "authType": "none", + "category": "tools", + "integrationType": "analytics", + "tags": ["cloud", "data-analytics"] + }, { "type": "attio", "slug": "attio", diff --git a/apps/sim/app/api/tools/athena/create-named-query/route.ts b/apps/sim/app/api/tools/athena/create-named-query/route.ts new file mode 100644 index 00000000000..f49bc16247d --- /dev/null +++ b/apps/sim/app/api/tools/athena/create-named-query/route.ts @@ -0,0 +1,69 @@ +import { CreateNamedQueryCommand } from '@aws-sdk/client-athena' +import { createLogger } from '@sim/logger' +import { type NextRequest, NextResponse } from 'next/server' +import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' +import { createAthenaClient } from '@/app/api/tools/athena/utils' + +const logger = createLogger('AthenaCreateNamedQuery') + +const CreateNamedQuerySchema = 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'), + name: z.string().min(1, 'Query name is required'), + database: z.string().min(1, 'Database is required'), + queryString: z.string().min(1, 'Query string is required'), + description: z.string().optional(), + workGroup: 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 data = CreateNamedQuerySchema.parse(body) + + const client = createAthenaClient({ + region: data.region, + accessKeyId: data.accessKeyId, + secretAccessKey: data.secretAccessKey, + }) + + const command = new CreateNamedQueryCommand({ + Name: data.name, + Database: data.database, + QueryString: data.queryString, + ...(data.description && { Description: data.description }), + ...(data.workGroup && { WorkGroup: data.workGroup }), + }) + + const response = await client.send(command) + + if (!response.NamedQueryId) { + throw new Error('No named query ID returned') + } + + return NextResponse.json({ + success: true, + output: { + namedQueryId: response.NamedQueryId, + }, + }) + } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } + const errorMessage = + error instanceof Error ? error.message : 'Failed to create Athena named query' + logger.error('CreateNamedQuery failed', { error: errorMessage }) + return NextResponse.json({ error: errorMessage }, { status: 500 }) + } +} diff --git a/apps/sim/app/api/tools/athena/get-named-query/route.ts b/apps/sim/app/api/tools/athena/get-named-query/route.ts new file mode 100644 index 00000000000..394bbda2b87 --- /dev/null +++ b/apps/sim/app/api/tools/athena/get-named-query/route.ts @@ -0,0 +1,66 @@ +import { GetNamedQueryCommand } from '@aws-sdk/client-athena' +import { createLogger } from '@sim/logger' +import { type NextRequest, NextResponse } from 'next/server' +import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' +import { createAthenaClient } from '@/app/api/tools/athena/utils' + +const logger = createLogger('AthenaGetNamedQuery') + +const GetNamedQuerySchema = 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'), + namedQueryId: z.string().min(1, 'Named query 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 data = GetNamedQuerySchema.parse(body) + + const client = createAthenaClient({ + region: data.region, + accessKeyId: data.accessKeyId, + secretAccessKey: data.secretAccessKey, + }) + + const command = new GetNamedQueryCommand({ + NamedQueryId: data.namedQueryId, + }) + + const response = await client.send(command) + const namedQuery = response.NamedQuery + + if (!namedQuery) { + throw new Error('No named query data returned') + } + + return NextResponse.json({ + success: true, + output: { + namedQueryId: namedQuery.NamedQueryId ?? data.namedQueryId, + name: namedQuery.Name ?? '', + description: namedQuery.Description ?? null, + database: namedQuery.Database ?? '', + queryString: namedQuery.QueryString ?? '', + workGroup: namedQuery.WorkGroup ?? null, + }, + }) + } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } + const errorMessage = error instanceof Error ? error.message : 'Failed to get Athena named query' + logger.error('GetNamedQuery failed', { error: errorMessage }) + return NextResponse.json({ error: errorMessage }, { status: 500 }) + } +} diff --git a/apps/sim/app/api/tools/athena/get-query-execution/route.ts b/apps/sim/app/api/tools/athena/get-query-execution/route.ts new file mode 100644 index 00000000000..129f4794dc4 --- /dev/null +++ b/apps/sim/app/api/tools/athena/get-query-execution/route.ts @@ -0,0 +1,77 @@ +import { GetQueryExecutionCommand } from '@aws-sdk/client-athena' +import { createLogger } from '@sim/logger' +import { type NextRequest, NextResponse } from 'next/server' +import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' +import { createAthenaClient } from '@/app/api/tools/athena/utils' + +const logger = createLogger('AthenaGetQueryExecution') + +const GetQueryExecutionSchema = 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'), + queryExecutionId: z.string().min(1, 'Query execution 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 data = GetQueryExecutionSchema.parse(body) + + const client = createAthenaClient({ + region: data.region, + accessKeyId: data.accessKeyId, + secretAccessKey: data.secretAccessKey, + }) + + const command = new GetQueryExecutionCommand({ + QueryExecutionId: data.queryExecutionId, + }) + + const response = await client.send(command) + const execution = response.QueryExecution + + if (!execution) { + throw new Error('No query execution data returned') + } + + return NextResponse.json({ + success: true, + output: { + queryExecutionId: execution.QueryExecutionId ?? data.queryExecutionId, + query: execution.Query ?? '', + state: execution.Status?.State ?? 'UNKNOWN', + stateChangeReason: execution.Status?.StateChangeReason ?? null, + statementType: execution.StatementType ?? null, + database: execution.QueryExecutionContext?.Database ?? null, + catalog: execution.QueryExecutionContext?.Catalog ?? null, + workGroup: execution.WorkGroup ?? null, + submissionDateTime: execution.Status?.SubmissionDateTime?.getTime() ?? null, + completionDateTime: execution.Status?.CompletionDateTime?.getTime() ?? null, + dataScannedInBytes: execution.Statistics?.DataScannedInBytes ?? null, + engineExecutionTimeInMillis: execution.Statistics?.EngineExecutionTimeInMillis ?? null, + queryPlanningTimeInMillis: execution.Statistics?.QueryPlanningTimeInMillis ?? null, + queryQueueTimeInMillis: execution.Statistics?.QueryQueueTimeInMillis ?? null, + totalExecutionTimeInMillis: execution.Statistics?.TotalExecutionTimeInMillis ?? null, + outputLocation: execution.ResultConfiguration?.OutputLocation ?? null, + }, + }) + } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } + const errorMessage = + error instanceof Error ? error.message : 'Failed to get Athena query execution' + logger.error('GetQueryExecution failed', { error: errorMessage }) + return NextResponse.json({ error: errorMessage }, { status: 500 }) + } +} diff --git a/apps/sim/app/api/tools/athena/get-query-results/route.ts b/apps/sim/app/api/tools/athena/get-query-results/route.ts new file mode 100644 index 00000000000..260cc73dc0c --- /dev/null +++ b/apps/sim/app/api/tools/athena/get-query-results/route.ts @@ -0,0 +1,88 @@ +import { GetQueryResultsCommand } from '@aws-sdk/client-athena' +import { createLogger } from '@sim/logger' +import { type NextRequest, NextResponse } from 'next/server' +import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' +import { createAthenaClient } from '@/app/api/tools/athena/utils' + +const logger = createLogger('AthenaGetQueryResults') + +const GetQueryResultsSchema = 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'), + queryExecutionId: z.string().min(1, 'Query execution ID is required'), + maxResults: z.preprocess( + (v) => (v === '' || v === undefined || v === null ? undefined : v), + z.number({ coerce: true }).int().positive().max(999).optional() + ), + nextToken: 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 data = GetQueryResultsSchema.parse(body) + + const client = createAthenaClient({ + region: data.region, + accessKeyId: data.accessKeyId, + secretAccessKey: data.secretAccessKey, + }) + + const isFirstPage = !data.nextToken + const adjustedMaxResults = + data.maxResults !== undefined && isFirstPage ? data.maxResults + 1 : data.maxResults + + const command = new GetQueryResultsCommand({ + QueryExecutionId: data.queryExecutionId, + ...(adjustedMaxResults !== undefined && { MaxResults: adjustedMaxResults }), + ...(data.nextToken && { NextToken: data.nextToken }), + }) + + const response = await client.send(command) + + const columnInfo = response.ResultSet?.ResultSetMetadata?.ColumnInfo ?? [] + const columns = columnInfo.map((col) => ({ + name: col.Name ?? '', + type: col.Type ?? 'varchar', + })) + + const rawRows = response.ResultSet?.Rows ?? [] + const dataRows = data.nextToken ? rawRows : rawRows.slice(1) + const rows = dataRows.map((row) => { + const record: Record = {} + const rowData = row.Data ?? [] + for (let i = 0; i < columns.length; i++) { + record[columns[i].name] = rowData[i]?.VarCharValue ?? '' + } + return record + }) + + return NextResponse.json({ + success: true, + output: { + columns, + rows, + nextToken: response.NextToken ?? null, + updateCount: response.UpdateCount ?? null, + }, + }) + } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } + const errorMessage = + error instanceof Error ? error.message : 'Failed to get Athena query results' + logger.error('GetQueryResults failed', { error: errorMessage }) + return NextResponse.json({ error: errorMessage }, { status: 500 }) + } +} diff --git a/apps/sim/app/api/tools/athena/list-named-queries/route.ts b/apps/sim/app/api/tools/athena/list-named-queries/route.ts new file mode 100644 index 00000000000..326e03711bb --- /dev/null +++ b/apps/sim/app/api/tools/athena/list-named-queries/route.ts @@ -0,0 +1,65 @@ +import { ListNamedQueriesCommand } from '@aws-sdk/client-athena' +import { createLogger } from '@sim/logger' +import { type NextRequest, NextResponse } from 'next/server' +import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' +import { createAthenaClient } from '@/app/api/tools/athena/utils' + +const logger = createLogger('AthenaListNamedQueries') + +const ListNamedQueriesSchema = 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'), + workGroup: z.string().optional(), + maxResults: z.preprocess( + (v) => (v === '' || v === undefined || v === null ? undefined : v), + z.number({ coerce: true }).int().min(0).max(50).optional() + ), + nextToken: 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 data = ListNamedQueriesSchema.parse(body) + + const client = createAthenaClient({ + region: data.region, + accessKeyId: data.accessKeyId, + secretAccessKey: data.secretAccessKey, + }) + + const command = new ListNamedQueriesCommand({ + ...(data.workGroup && { WorkGroup: data.workGroup }), + ...(data.maxResults !== undefined && { MaxResults: data.maxResults }), + ...(data.nextToken && { NextToken: data.nextToken }), + }) + + const response = await client.send(command) + + return NextResponse.json({ + success: true, + output: { + namedQueryIds: response.NamedQueryIds ?? [], + nextToken: response.NextToken ?? null, + }, + }) + } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } + const errorMessage = + error instanceof Error ? error.message : 'Failed to list Athena named queries' + logger.error('ListNamedQueries failed', { error: errorMessage }) + return NextResponse.json({ error: errorMessage }, { status: 500 }) + } +} diff --git a/apps/sim/app/api/tools/athena/list-query-executions/route.ts b/apps/sim/app/api/tools/athena/list-query-executions/route.ts new file mode 100644 index 00000000000..958d09b1735 --- /dev/null +++ b/apps/sim/app/api/tools/athena/list-query-executions/route.ts @@ -0,0 +1,65 @@ +import { ListQueryExecutionsCommand } from '@aws-sdk/client-athena' +import { createLogger } from '@sim/logger' +import { type NextRequest, NextResponse } from 'next/server' +import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' +import { createAthenaClient } from '@/app/api/tools/athena/utils' + +const logger = createLogger('AthenaListQueryExecutions') + +const ListQueryExecutionsSchema = 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'), + workGroup: z.string().optional(), + maxResults: z.preprocess( + (v) => (v === '' || v === undefined || v === null ? undefined : v), + z.number({ coerce: true }).int().min(0).max(50).optional() + ), + nextToken: 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 data = ListQueryExecutionsSchema.parse(body) + + const client = createAthenaClient({ + region: data.region, + accessKeyId: data.accessKeyId, + secretAccessKey: data.secretAccessKey, + }) + + const command = new ListQueryExecutionsCommand({ + ...(data.workGroup && { WorkGroup: data.workGroup }), + ...(data.maxResults !== undefined && { MaxResults: data.maxResults }), + ...(data.nextToken && { NextToken: data.nextToken }), + }) + + const response = await client.send(command) + + return NextResponse.json({ + success: true, + output: { + queryExecutionIds: response.QueryExecutionIds ?? [], + nextToken: response.NextToken ?? null, + }, + }) + } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } + const errorMessage = + error instanceof Error ? error.message : 'Failed to list Athena query executions' + logger.error('ListQueryExecutions failed', { error: errorMessage }) + return NextResponse.json({ error: errorMessage }, { status: 500 }) + } +} diff --git a/apps/sim/app/api/tools/athena/start-query/route.ts b/apps/sim/app/api/tools/athena/start-query/route.ts new file mode 100644 index 00000000000..0555246aa8e --- /dev/null +++ b/apps/sim/app/api/tools/athena/start-query/route.ts @@ -0,0 +1,80 @@ +import { StartQueryExecutionCommand } from '@aws-sdk/client-athena' +import { createLogger } from '@sim/logger' +import { type NextRequest, NextResponse } from 'next/server' +import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' +import { createAthenaClient } from '@/app/api/tools/athena/utils' + +const logger = createLogger('AthenaStartQuery') + +const StartQuerySchema = 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'), + queryString: z.string().min(1, 'Query string is required'), + database: z.string().optional(), + catalog: z.string().optional(), + outputLocation: z.string().optional(), + workGroup: 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 data = StartQuerySchema.parse(body) + + const client = createAthenaClient({ + region: data.region, + accessKeyId: data.accessKeyId, + secretAccessKey: data.secretAccessKey, + }) + + const command = new StartQueryExecutionCommand({ + QueryString: data.queryString, + ...(data.database || data.catalog + ? { + QueryExecutionContext: { + ...(data.database && { Database: data.database }), + ...(data.catalog && { Catalog: data.catalog }), + }, + } + : {}), + ...(data.outputLocation + ? { + ResultConfiguration: { + OutputLocation: data.outputLocation, + }, + } + : {}), + ...(data.workGroup && { WorkGroup: data.workGroup }), + }) + + const response = await client.send(command) + + if (!response.QueryExecutionId) { + throw new Error('No query execution ID returned') + } + + return NextResponse.json({ + success: true, + output: { + queryExecutionId: response.QueryExecutionId, + }, + }) + } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } + const errorMessage = error instanceof Error ? error.message : 'Failed to start Athena query' + logger.error('StartQuery failed', { error: errorMessage }) + return NextResponse.json({ error: errorMessage }, { status: 500 }) + } +} diff --git a/apps/sim/app/api/tools/athena/stop-query/route.ts b/apps/sim/app/api/tools/athena/stop-query/route.ts new file mode 100644 index 00000000000..0a7558d314a --- /dev/null +++ b/apps/sim/app/api/tools/athena/stop-query/route.ts @@ -0,0 +1,56 @@ +import { StopQueryExecutionCommand } from '@aws-sdk/client-athena' +import { createLogger } from '@sim/logger' +import { type NextRequest, NextResponse } from 'next/server' +import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' +import { createAthenaClient } from '@/app/api/tools/athena/utils' + +const logger = createLogger('AthenaStopQuery') + +const StopQuerySchema = 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'), + queryExecutionId: z.string().min(1, 'Query execution 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 data = StopQuerySchema.parse(body) + + const client = createAthenaClient({ + region: data.region, + accessKeyId: data.accessKeyId, + secretAccessKey: data.secretAccessKey, + }) + + const command = new StopQueryExecutionCommand({ + QueryExecutionId: data.queryExecutionId, + }) + + await client.send(command) + + return NextResponse.json({ + success: true, + output: { + success: true, + }, + }) + } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } + const errorMessage = error instanceof Error ? error.message : 'Failed to stop Athena query' + logger.error('StopQuery failed', { error: errorMessage }) + return NextResponse.json({ error: errorMessage }, { status: 500 }) + } +} diff --git a/apps/sim/app/api/tools/athena/utils.ts b/apps/sim/app/api/tools/athena/utils.ts new file mode 100644 index 00000000000..1d52a2ffe64 --- /dev/null +++ b/apps/sim/app/api/tools/athena/utils.ts @@ -0,0 +1,17 @@ +import { AthenaClient } from '@aws-sdk/client-athena' + +interface AwsCredentials { + region: string + accessKeyId: string + secretAccessKey: string +} + +export function createAthenaClient(config: AwsCredentials): AthenaClient { + return new AthenaClient({ + region: config.region, + credentials: { + accessKeyId: config.accessKeyId, + secretAccessKey: config.secretAccessKey, + }, + }) +} 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 index b47611b0cfb..d267bb01fa3 100644 --- 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 @@ -53,6 +53,12 @@ export async function POST(request: NextRequest) { }, }) } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } const errorMessage = error instanceof Error ? error.message : 'Failed to describe stack drift detection status' logger.error('DescribeStackDriftDetectionStatus failed', { error: errorMessage }) 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 index b838512a437..a3108eebffc 100644 --- a/apps/sim/app/api/tools/cloudformation/describe-stack-events/route.ts +++ b/apps/sim/app/api/tools/cloudformation/describe-stack-events/route.ts @@ -70,6 +70,12 @@ export async function POST(request: NextRequest) { output: { events }, }) } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } const errorMessage = error instanceof Error ? error.message : 'Failed to describe CloudFormation stack events' logger.error('DescribeStackEvents failed', { error: errorMessage }) diff --git a/apps/sim/app/api/tools/cloudformation/describe-stacks/route.ts b/apps/sim/app/api/tools/cloudformation/describe-stacks/route.ts index 30cf207d7e6..d8fc946b519 100644 --- a/apps/sim/app/api/tools/cloudformation/describe-stacks/route.ts +++ b/apps/sim/app/api/tools/cloudformation/describe-stacks/route.ts @@ -78,6 +78,12 @@ export async function POST(request: NextRequest) { output: { stacks }, }) } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } const errorMessage = error instanceof Error ? error.message : 'Failed to describe CloudFormation stacks' logger.error('DescribeStacks failed', { error: errorMessage }) 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 index 30d05166816..a21c3e70410 100644 --- a/apps/sim/app/api/tools/cloudformation/detect-stack-drift/route.ts +++ b/apps/sim/app/api/tools/cloudformation/detect-stack-drift/route.ts @@ -48,6 +48,12 @@ export async function POST(request: NextRequest) { }, }) } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } const errorMessage = error instanceof Error ? error.message : 'Failed to detect CloudFormation stack drift' logger.error('DetectStackDrift failed', { error: errorMessage }) diff --git a/apps/sim/app/api/tools/cloudformation/get-template/route.ts b/apps/sim/app/api/tools/cloudformation/get-template/route.ts index 9abdad4e170..a5e6edeeaa3 100644 --- a/apps/sim/app/api/tools/cloudformation/get-template/route.ts +++ b/apps/sim/app/api/tools/cloudformation/get-template/route.ts @@ -45,6 +45,12 @@ export async function POST(request: NextRequest) { }, }) } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } const errorMessage = error instanceof Error ? error.message : 'Failed to get CloudFormation template' logger.error('GetTemplate failed', { error: errorMessage }) 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 index ca22c8e8567..dfc65171362 100644 --- a/apps/sim/app/api/tools/cloudformation/list-stack-resources/route.ts +++ b/apps/sim/app/api/tools/cloudformation/list-stack-resources/route.ts @@ -67,6 +67,12 @@ export async function POST(request: NextRequest) { output: { resources }, }) } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } const errorMessage = error instanceof Error ? error.message : 'Failed to list CloudFormation stack resources' logger.error('ListStackResources failed', { error: errorMessage }) diff --git a/apps/sim/app/api/tools/cloudformation/validate-template/route.ts b/apps/sim/app/api/tools/cloudformation/validate-template/route.ts index e2a8b4428ed..1264d813fdf 100644 --- a/apps/sim/app/api/tools/cloudformation/validate-template/route.ts +++ b/apps/sim/app/api/tools/cloudformation/validate-template/route.ts @@ -53,6 +53,12 @@ export async function POST(request: NextRequest) { }, }) } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } const errorMessage = error instanceof Error ? error.message : 'Failed to validate CloudFormation template' logger.error('ValidateTemplate failed', { error: errorMessage }) diff --git a/apps/sim/app/api/tools/cloudwatch/describe-alarms/route.ts b/apps/sim/app/api/tools/cloudwatch/describe-alarms/route.ts index 0ab6c4aad8d..3fc65ab5bfd 100644 --- a/apps/sim/app/api/tools/cloudwatch/describe-alarms/route.ts +++ b/apps/sim/app/api/tools/cloudwatch/describe-alarms/route.ts @@ -88,6 +88,12 @@ export async function POST(request: NextRequest) { output: { alarms: [...metricAlarms, ...compositeAlarms] }, }) } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } const errorMessage = error instanceof Error ? error.message : 'Failed to describe CloudWatch alarms' logger.error('DescribeAlarms failed', { error: errorMessage }) diff --git a/apps/sim/app/api/tools/cloudwatch/describe-log-groups/route.ts b/apps/sim/app/api/tools/cloudwatch/describe-log-groups/route.ts index a10f46c4efa..fcb29be5289 100644 --- a/apps/sim/app/api/tools/cloudwatch/describe-log-groups/route.ts +++ b/apps/sim/app/api/tools/cloudwatch/describe-log-groups/route.ts @@ -54,6 +54,12 @@ export async function POST(request: NextRequest) { output: { logGroups }, }) } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } const errorMessage = error instanceof Error ? error.message : 'Failed to describe CloudWatch log groups' logger.error('DescribeLogGroups failed', { error: errorMessage }) diff --git a/apps/sim/app/api/tools/cloudwatch/describe-log-streams/route.ts b/apps/sim/app/api/tools/cloudwatch/describe-log-streams/route.ts index d74ad6de266..223e51617b9 100644 --- a/apps/sim/app/api/tools/cloudwatch/describe-log-streams/route.ts +++ b/apps/sim/app/api/tools/cloudwatch/describe-log-streams/route.ts @@ -44,6 +44,12 @@ export async function POST(request: NextRequest) { output: { logStreams: result.logStreams }, }) } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } const errorMessage = error instanceof Error ? error.message : 'Failed to describe CloudWatch log streams' logger.error('DescribeLogStreams failed', { error: errorMessage }) diff --git a/apps/sim/app/api/tools/cloudwatch/get-log-events/route.ts b/apps/sim/app/api/tools/cloudwatch/get-log-events/route.ts index c42d49e97ab..e1a8abcf666 100644 --- a/apps/sim/app/api/tools/cloudwatch/get-log-events/route.ts +++ b/apps/sim/app/api/tools/cloudwatch/get-log-events/route.ts @@ -52,6 +52,12 @@ export async function POST(request: NextRequest) { output: { events: result.events }, }) } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } const errorMessage = error instanceof Error ? error.message : 'Failed to get CloudWatch log events' logger.error('GetLogEvents failed', { error: errorMessage }) 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 321092b3283..677bafca3ca 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 @@ -89,6 +89,12 @@ export async function POST(request: NextRequest) { }, }) } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } const errorMessage = error instanceof Error ? error.message : 'Failed to get CloudWatch metric statistics' logger.error('GetMetricStatistics failed', { error: errorMessage }) 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 09485cb8590..36d2c31e2fa 100644 --- a/apps/sim/app/api/tools/cloudwatch/list-metrics/route.ts +++ b/apps/sim/app/api/tools/cloudwatch/list-metrics/route.ts @@ -62,6 +62,12 @@ export async function POST(request: NextRequest) { output: { metrics }, }) } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } const errorMessage = error instanceof Error ? error.message : 'Failed to list CloudWatch metrics' logger.error('ListMetrics failed', { error: errorMessage }) diff --git a/apps/sim/app/api/tools/cloudwatch/query-logs/route.ts b/apps/sim/app/api/tools/cloudwatch/query-logs/route.ts index a471edc20e8..75b4dab2395 100644 --- a/apps/sim/app/api/tools/cloudwatch/query-logs/route.ts +++ b/apps/sim/app/api/tools/cloudwatch/query-logs/route.ts @@ -63,6 +63,12 @@ export async function POST(request: NextRequest) { }, }) } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } const errorMessage = error instanceof Error ? error.message : 'CloudWatch Log Insights query failed' logger.error('QueryLogs failed', { error: errorMessage }) diff --git a/apps/sim/app/api/tools/dynamodb/delete/route.ts b/apps/sim/app/api/tools/dynamodb/delete/route.ts index 0002787f373..5b6ab1d5b20 100644 --- a/apps/sim/app/api/tools/dynamodb/delete/route.ts +++ b/apps/sim/app/api/tools/dynamodb/delete/route.ts @@ -41,6 +41,12 @@ export async function POST(request: NextRequest) { message: 'Item deleted successfully', }) } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } const errorMessage = error instanceof Error ? error.message : 'DynamoDB delete failed' return NextResponse.json({ error: errorMessage }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/dynamodb/get/route.ts b/apps/sim/app/api/tools/dynamodb/get/route.ts index 851f1cb0740..1eca9d3f72e 100644 --- a/apps/sim/app/api/tools/dynamodb/get/route.ts +++ b/apps/sim/app/api/tools/dynamodb/get/route.ts @@ -48,6 +48,12 @@ export async function POST(request: NextRequest) { item: result.item, }) } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } const errorMessage = error instanceof Error ? error.message : 'DynamoDB get failed' return NextResponse.json({ error: errorMessage }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/dynamodb/put/route.ts b/apps/sim/app/api/tools/dynamodb/put/route.ts index cb001c5873a..2572cdcd5e7 100644 --- a/apps/sim/app/api/tools/dynamodb/put/route.ts +++ b/apps/sim/app/api/tools/dynamodb/put/route.ts @@ -36,6 +36,12 @@ export async function POST(request: NextRequest) { item: validatedData.item, }) } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } const errorMessage = error instanceof Error ? error.message : 'DynamoDB put failed' return NextResponse.json({ error: errorMessage }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/dynamodb/query/route.ts b/apps/sim/app/api/tools/dynamodb/query/route.ts index 06945c10070..3b1fadeee12 100644 --- a/apps/sim/app/api/tools/dynamodb/query/route.ts +++ b/apps/sim/app/api/tools/dynamodb/query/route.ts @@ -51,6 +51,12 @@ export async function POST(request: NextRequest) { count: result.count, }) } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } const errorMessage = error instanceof Error ? error.message : 'DynamoDB query failed' return NextResponse.json({ error: errorMessage }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/dynamodb/scan/route.ts b/apps/sim/app/api/tools/dynamodb/scan/route.ts index c083faad584..64c47895b0a 100644 --- a/apps/sim/app/api/tools/dynamodb/scan/route.ts +++ b/apps/sim/app/api/tools/dynamodb/scan/route.ts @@ -45,6 +45,12 @@ export async function POST(request: NextRequest) { count: result.count, }) } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } const errorMessage = error instanceof Error ? error.message : 'DynamoDB scan failed' return NextResponse.json({ error: errorMessage }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/dynamodb/update/route.ts b/apps/sim/app/api/tools/dynamodb/update/route.ts index 07abcc25648..3a5892fe61a 100644 --- a/apps/sim/app/api/tools/dynamodb/update/route.ts +++ b/apps/sim/app/api/tools/dynamodb/update/route.ts @@ -50,6 +50,12 @@ export async function POST(request: NextRequest) { item: result.attributes, }) } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } const errorMessage = error instanceof Error ? error.message : 'DynamoDB update failed' return NextResponse.json({ error: errorMessage }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/google_drive/download/route.ts b/apps/sim/app/api/tools/google_drive/download/route.ts index 2a9730dcada..e4131423f91 100644 --- a/apps/sim/app/api/tools/google_drive/download/route.ts +++ b/apps/sim/app/api/tools/google_drive/download/route.ts @@ -240,6 +240,12 @@ export async function POST(request: NextRequest) { }, }) } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { success: false, error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } logger.error(`[${requestId}] Error downloading Google Drive file:`, error) return NextResponse.json( { diff --git a/apps/sim/app/api/tools/onedrive/download/route.ts b/apps/sim/app/api/tools/onedrive/download/route.ts index a50338af557..2cc268ffd5e 100644 --- a/apps/sim/app/api/tools/onedrive/download/route.ts +++ b/apps/sim/app/api/tools/onedrive/download/route.ts @@ -165,6 +165,12 @@ export async function POST(request: NextRequest) { }, }) } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { success: false, error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } logger.error(`[${requestId}] Error downloading OneDrive file:`, error) return NextResponse.json( { diff --git a/apps/sim/app/api/tools/outlook/draft/route.ts b/apps/sim/app/api/tools/outlook/draft/route.ts index eeee0f14e17..801b3b90869 100644 --- a/apps/sim/app/api/tools/outlook/draft/route.ts +++ b/apps/sim/app/api/tools/outlook/draft/route.ts @@ -176,6 +176,12 @@ export async function POST(request: NextRequest) { }, }) } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { success: false, error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } logger.error(`[${requestId}] Error creating Outlook draft:`, error) return NextResponse.json( { diff --git a/apps/sim/app/api/tools/outlook/send/route.ts b/apps/sim/app/api/tools/outlook/send/route.ts index f90f62518a8..f2d39ef11e6 100644 --- a/apps/sim/app/api/tools/outlook/send/route.ts +++ b/apps/sim/app/api/tools/outlook/send/route.ts @@ -189,6 +189,12 @@ export async function POST(request: NextRequest) { }, }) } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { success: false, error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } logger.error(`[${requestId}] Error sending Outlook email:`, error) return NextResponse.json( { diff --git a/apps/sim/app/api/tools/slack/download/route.ts b/apps/sim/app/api/tools/slack/download/route.ts index 45c34bcd11d..83a44386d4d 100644 --- a/apps/sim/app/api/tools/slack/download/route.ts +++ b/apps/sim/app/api/tools/slack/download/route.ts @@ -158,6 +158,12 @@ export async function POST(request: NextRequest) { }, }) } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { success: false, error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } logger.error(`[${requestId}] Error downloading Slack file:`, error) return NextResponse.json( { diff --git a/apps/sim/app/api/tools/slack/send-ephemeral/route.ts b/apps/sim/app/api/tools/slack/send-ephemeral/route.ts index 6d443e5039a..1387290c6ae 100644 --- a/apps/sim/app/api/tools/slack/send-ephemeral/route.ts +++ b/apps/sim/app/api/tools/slack/send-ephemeral/route.ts @@ -84,6 +84,12 @@ export async function POST(request: NextRequest) { }, }) } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { success: false, error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } logger.error(`[${requestId}] Error sending ephemeral message:`, error) return NextResponse.json( { diff --git a/apps/sim/app/api/tools/slack/send-message/route.ts b/apps/sim/app/api/tools/slack/send-message/route.ts index a6b8a3db71c..5520a280f6e 100644 --- a/apps/sim/app/api/tools/slack/send-message/route.ts +++ b/apps/sim/app/api/tools/slack/send-message/route.ts @@ -77,6 +77,12 @@ export async function POST(request: NextRequest) { return NextResponse.json({ success: true, output: result.output }) } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { success: false, error: error.errors[0]?.message ?? 'Invalid request' }, + { status: 400 } + ) + } logger.error(`[${requestId}] Error sending Slack message:`, error) return NextResponse.json( { diff --git a/apps/sim/app/api/webhooks/poll/rss/route.ts b/apps/sim/app/api/webhooks/poll/[provider]/route.ts similarity index 55% rename from apps/sim/app/api/webhooks/poll/rss/route.ts rename to apps/sim/app/api/webhooks/poll/[provider]/route.ts index cd221abe394..d314e8563bb 100644 --- a/apps/sim/app/api/webhooks/poll/rss/route.ts +++ b/apps/sim/app/api/webhooks/poll/[provider]/route.ts @@ -3,31 +3,36 @@ import { type NextRequest, NextResponse } from 'next/server' import { verifyCronAuth } from '@/lib/auth/internal' import { acquireLock, releaseLock } from '@/lib/core/config/redis' import { generateShortId } from '@/lib/core/utils/uuid' -import { pollRssWebhooks } from '@/lib/webhooks/rss-polling-service' +import { pollProvider, VALID_POLLING_PROVIDERS } from '@/lib/webhooks/polling' -const logger = createLogger('RssPollingAPI') +const logger = createLogger('PollingAPI') -export const dynamic = 'force-dynamic' -export const maxDuration = 180 // Allow up to 3 minutes for polling to complete +/** Lock TTL in seconds — must match maxDuration so the lock auto-expires if the function times out. */ +const LOCK_TTL_SECONDS = 180 -const LOCK_KEY = 'rss-polling-lock' -const LOCK_TTL_SECONDS = 180 // Same as maxDuration (3 min) +export const dynamic = 'force-dynamic' +export const maxDuration = 180 -export async function GET(request: NextRequest) { +export async function GET( + request: NextRequest, + { params }: { params: Promise<{ provider: string }> } +) { + const { provider } = await params const requestId = generateShortId() - logger.info(`RSS webhook polling triggered (${requestId})`) + const LOCK_KEY = `${provider}-polling-lock` let lockValue: string | undefined try { - const authError = verifyCronAuth(request, 'RSS webhook polling') - if (authError) { - return authError + const authError = verifyCronAuth(request, `${provider} webhook polling`) + if (authError) return authError + + if (!VALID_POLLING_PROVIDERS.has(provider)) { + return NextResponse.json({ error: `Unknown polling provider: ${provider}` }, { status: 404 }) } lockValue = requestId const locked = await acquireLock(LOCK_KEY, lockValue, LOCK_TTL_SECONDS) - if (!locked) { return NextResponse.json( { @@ -40,21 +45,21 @@ export async function GET(request: NextRequest) { ) } - const results = await pollRssWebhooks() + const results = await pollProvider(provider) return NextResponse.json({ success: true, - message: 'RSS polling completed', + message: `${provider} polling completed`, requestId, status: 'completed', ...results, }) } catch (error) { - logger.error(`Error during RSS polling (${requestId}):`, error) + logger.error(`Error during ${provider} polling (${requestId}):`, error) return NextResponse.json( { success: false, - message: 'RSS polling failed', + message: `${provider} polling failed`, error: error instanceof Error ? error.message : 'Unknown error', requestId, }, diff --git a/apps/sim/app/api/webhooks/poll/gmail/route.ts b/apps/sim/app/api/webhooks/poll/gmail/route.ts deleted file mode 100644 index 5eabf8b9b63..00000000000 --- a/apps/sim/app/api/webhooks/poll/gmail/route.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { createLogger } from '@sim/logger' -import { type NextRequest, NextResponse } from 'next/server' -import { verifyCronAuth } from '@/lib/auth/internal' -import { acquireLock, releaseLock } from '@/lib/core/config/redis' -import { generateShortId } from '@/lib/core/utils/uuid' -import { pollGmailWebhooks } from '@/lib/webhooks/gmail-polling-service' - -const logger = createLogger('GmailPollingAPI') - -export const dynamic = 'force-dynamic' -export const maxDuration = 180 // Allow up to 3 minutes for polling to complete - -const LOCK_KEY = 'gmail-polling-lock' -const LOCK_TTL_SECONDS = 180 // Same as maxDuration (3 min) - -export async function GET(request: NextRequest) { - const requestId = generateShortId() - logger.info(`Gmail webhook polling triggered (${requestId})`) - - let lockValue: string | undefined - - try { - const authError = verifyCronAuth(request, 'Gmail webhook polling') - if (authError) { - return authError - } - - lockValue = requestId // unique value to identify the holder - const locked = await acquireLock(LOCK_KEY, lockValue, LOCK_TTL_SECONDS) - - if (!locked) { - return NextResponse.json( - { - success: true, - message: 'Polling already in progress – skipped', - requestId, - status: 'skip', - }, - { status: 202 } - ) - } - - const results = await pollGmailWebhooks() - - return NextResponse.json({ - success: true, - message: 'Gmail polling completed', - requestId, - status: 'completed', - ...results, - }) - } catch (error) { - logger.error(`Error during Gmail polling (${requestId}):`, error) - return NextResponse.json( - { - success: false, - message: 'Gmail polling failed', - error: error instanceof Error ? error.message : 'Unknown error', - requestId, - }, - { status: 500 } - ) - } finally { - if (lockValue) { - await releaseLock(LOCK_KEY, lockValue).catch(() => {}) - } - } -} diff --git a/apps/sim/app/api/webhooks/poll/imap/route.ts b/apps/sim/app/api/webhooks/poll/imap/route.ts deleted file mode 100644 index 29826e04bff..00000000000 --- a/apps/sim/app/api/webhooks/poll/imap/route.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { createLogger } from '@sim/logger' -import { type NextRequest, NextResponse } from 'next/server' -import { verifyCronAuth } from '@/lib/auth/internal' -import { acquireLock, releaseLock } from '@/lib/core/config/redis' -import { generateShortId } from '@/lib/core/utils/uuid' -import { pollImapWebhooks } from '@/lib/webhooks/imap-polling-service' - -const logger = createLogger('ImapPollingAPI') - -export const dynamic = 'force-dynamic' -export const maxDuration = 180 // Allow up to 3 minutes for polling to complete - -const LOCK_KEY = 'imap-polling-lock' -const LOCK_TTL_SECONDS = 180 // Same as maxDuration (3 min) - -export async function GET(request: NextRequest) { - const requestId = generateShortId() - logger.info(`IMAP webhook polling triggered (${requestId})`) - - let lockValue: string | undefined - - try { - const authError = verifyCronAuth(request, 'IMAP webhook polling') - if (authError) { - return authError - } - - lockValue = requestId // unique value to identify the holder - const locked = await acquireLock(LOCK_KEY, lockValue, LOCK_TTL_SECONDS) - - if (!locked) { - return NextResponse.json( - { - success: true, - message: 'Polling already in progress – skipped', - requestId, - status: 'skip', - }, - { status: 202 } - ) - } - - const results = await pollImapWebhooks() - - return NextResponse.json({ - success: true, - message: 'IMAP polling completed', - requestId, - status: 'completed', - ...results, - }) - } catch (error) { - logger.error(`Error during IMAP polling (${requestId}):`, error) - return NextResponse.json( - { - success: false, - message: 'IMAP polling failed', - error: error instanceof Error ? error.message : 'Unknown error', - requestId, - }, - { status: 500 } - ) - } finally { - if (lockValue) { - await releaseLock(LOCK_KEY, lockValue).catch(() => {}) - } - } -} diff --git a/apps/sim/app/api/webhooks/poll/outlook/route.ts b/apps/sim/app/api/webhooks/poll/outlook/route.ts deleted file mode 100644 index 2a72c34f86a..00000000000 --- a/apps/sim/app/api/webhooks/poll/outlook/route.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { createLogger } from '@sim/logger' -import { type NextRequest, NextResponse } from 'next/server' -import { verifyCronAuth } from '@/lib/auth/internal' -import { acquireLock, releaseLock } from '@/lib/core/config/redis' -import { generateShortId } from '@/lib/core/utils/uuid' -import { pollOutlookWebhooks } from '@/lib/webhooks/outlook-polling-service' - -const logger = createLogger('OutlookPollingAPI') - -export const dynamic = 'force-dynamic' -export const maxDuration = 180 // Allow up to 3 minutes for polling to complete - -const LOCK_KEY = 'outlook-polling-lock' -const LOCK_TTL_SECONDS = 180 // Same as maxDuration (3 min) - -export async function GET(request: NextRequest) { - const requestId = generateShortId() - logger.info(`Outlook webhook polling triggered (${requestId})`) - - let lockValue: string | undefined - - try { - const authError = verifyCronAuth(request, 'Outlook webhook polling') - if (authError) { - return authError - } - - lockValue = requestId // unique value to identify the holder - const locked = await acquireLock(LOCK_KEY, lockValue, LOCK_TTL_SECONDS) - - if (!locked) { - return NextResponse.json( - { - success: true, - message: 'Polling already in progress – skipped', - requestId, - status: 'skip', - }, - { status: 202 } - ) - } - - const results = await pollOutlookWebhooks() - - return NextResponse.json({ - success: true, - message: 'Outlook polling completed', - requestId, - status: 'completed', - ...results, - }) - } catch (error) { - logger.error(`Error during Outlook polling (${requestId}):`, error) - return NextResponse.json( - { - success: false, - message: 'Outlook polling failed', - error: error instanceof Error ? error.message : 'Unknown error', - requestId, - }, - { status: 500 } - ) - } finally { - if (lockValue) { - await releaseLock(LOCK_KEY, lockValue).catch(() => {}) - } - } -} diff --git a/apps/sim/app/chat/components/message/components/markdown-renderer.tsx b/apps/sim/app/chat/components/message/components/markdown-renderer.tsx index 88a34da84e8..a62c901ae2f 100644 --- a/apps/sim/app/chat/components/message/components/markdown-renderer.tsx +++ b/apps/sim/app/chat/components/message/components/markdown-renderer.tsx @@ -2,6 +2,8 @@ import React, { type HTMLAttributes, memo, type ReactNode, useMemo } from 'react import ReactMarkdown from 'react-markdown' import remarkGfm from 'remark-gfm' import { Tooltip } from '@/components/emcn' +import { CopyCodeButton } from '@/components/ui/copy-code-button' +import { extractTextContent } from '@/lib/core/utils/react-node-text' export function LinkWithPreview({ href, children }: { href: string; children: React.ReactNode }) { return ( @@ -102,6 +104,10 @@ function createCustomComponents(LinkComponent: typeof LinkWithPreview) { {codeProps.className?.replace('language-', '') || 'code'} +
             {codeContent}
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/context-mention-icon.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/context-mention-icon.tsx
new file mode 100644
index 00000000000..1e7a2514620
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/context-mention-icon.tsx
@@ -0,0 +1,45 @@
+import { Blimp, Database, Folder as FolderIcon, Table as TableIcon } from '@/components/emcn/icons'
+import { getDocumentIcon } from '@/components/icons/document-icons'
+import { cn } from '@/lib/core/utils/cn'
+import { workflowBorderColor } from '@/lib/workspaces/colors'
+import type { ChatMessageContext } from '@/app/workspace/[workspaceId]/home/types'
+
+interface ContextMentionIconProps {
+  context: ChatMessageContext
+  /** Only used when context.kind is 'workflow' or 'current_workflow'; ignored otherwise. */
+  workflowColor?: string | null
+  /** Applied to every icon element. Include sizing and positional classes (e.g. h-[12px] w-[12px]). */
+  className: string
+}
+
+/** Renders the icon for a context mention chip. Returns null when no icon applies. */
+export function ContextMentionIcon({ context, workflowColor, className }: ContextMentionIconProps) {
+  switch (context.kind) {
+    case 'workflow':
+    case 'current_workflow':
+      return workflowColor ? (
+        
+      ) : null
+    case 'knowledge':
+      return 
+    case 'table':
+      return 
+    case 'file': {
+      const FileDocIcon = getDocumentIcon('', context.label)
+      return 
+    }
+    case 'folder':
+      return 
+    case 'past_chat':
+      return 
+    default:
+      return null
+  }
+}
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/index.ts b/apps/sim/app/workspace/[workspaceId]/home/components/index.ts
index 209ca78170d..38debef7f14 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/index.ts
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/index.ts
@@ -1,4 +1,5 @@
 export { ChatMessageAttachments } from './chat-message-attachments'
+export { ContextMentionIcon } from './context-mention-icon'
 export {
   assistantMessageHasRenderableContent,
   MessageContent,
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/chat-content/chat-content.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/chat-content/chat-content.tsx
index 8249422b73b..018967deae2 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/chat-content/chat-content.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/chat-content/chat-content.tsx
@@ -9,7 +9,9 @@ import 'prismjs/components/prism-css'
 import 'prismjs/components/prism-markup'
 import '@/components/emcn/components/code/code.css'
 import { Checkbox, highlight, languages } from '@/components/emcn'
+import { CopyCodeButton } from '@/components/ui/copy-code-button'
 import { cn } from '@/lib/core/utils/cn'
+import { extractTextContent } from '@/lib/core/utils/react-node-text'
 import {
   PendingTagIndicator,
   parseSpecialTags,
@@ -33,16 +35,6 @@ const LANG_ALIASES: Record = {
   py: 'python',
 }
 
-function extractTextContent(node: React.ReactNode): string {
-  if (typeof node === 'string') return node
-  if (typeof node === 'number') return String(node)
-  if (!node) return ''
-  if (Array.isArray(node)) return node.map(extractTextContent).join('')
-  if (isValidElement(node))
-    return extractTextContent((node.props as { children?: React.ReactNode }).children)
-  return ''
-}
-
 const PROSE_CLASSES = cn(
   'prose prose-base dark:prose-invert max-w-none',
   'font-[family-name:var(--font-inter)] antialiased break-words font-[430] tracking-[0]',
@@ -125,11 +117,13 @@ const MARKDOWN_COMPONENTS: React.ComponentProps['component
 
     return (
       
- {language && ( -
- {language} -
- )} +
+ {language || 'code'} + +
 void
+  onContextRemove?: (context: ChatContext) => void
   editValue?: string
   onEditValueConsumed?: () => void
   layout?: 'mothership-view' | 'copilot-view'
@@ -83,6 +84,7 @@ export function MothershipChat({
   userId,
   chatId,
   onContextAdd,
+  onContextRemove,
   editValue,
   onEditValueConsumed,
   layout = 'mothership-view',
@@ -207,6 +209,7 @@ export function MothershipChat({
             isInitialView={false}
             userId={userId}
             onContextAdd={onContextAdd}
+            onContextRemove={onContextRemove}
             editValue={editValue}
             onEditValueConsumed={onEditValueConsumed}
             onEnterWhileEmpty={handleEnterWhileEmpty}
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/add-resource-dropdown.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/add-resource-dropdown.tsx
index 02bdf37822c..7345277cf6a 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/add-resource-dropdown.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/add-resource-dropdown.tsx
@@ -27,6 +27,7 @@ import type {
 import { useFolders } from '@/hooks/queries/folders'
 import { useKnowledgeBasesQuery } from '@/hooks/queries/kb/knowledge'
 import { useTablesList } from '@/hooks/queries/tables'
+import { useTasks } from '@/hooks/queries/tasks'
 import { useWorkflows } from '@/hooks/queries/workflows'
 import { useWorkspaceFiles } from '@/hooks/queries/workspace-files'
 
@@ -53,6 +54,7 @@ export function useAvailableResources(
   const { data: files = [] } = useWorkspaceFiles(workspaceId)
   const { data: knowledgeBases } = useKnowledgeBasesQuery(workspaceId)
   const { data: folders = [] } = useFolders(workspaceId)
+  const { data: tasks = [] } = useTasks(workspaceId)
 
   return useMemo(
     () => [
@@ -97,8 +99,16 @@ export function useAvailableResources(
           isOpen: existingKeys.has(`knowledgebase:${kb.id}`),
         })),
       },
+      {
+        type: 'task' as const,
+        items: tasks.map((t) => ({
+          id: t.id,
+          name: t.name,
+          isOpen: existingKeys.has(`task:${t.id}`),
+        })),
+      },
     ],
-    [workflows, folders, tables, files, knowledgeBases, existingKeys]
+    [workflows, folders, tables, files, knowledgeBases, tasks, existingKeys]
   )
 }
 
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx
index e9fb56844ed..ab2655ed48d 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx
@@ -22,6 +22,7 @@ import {
   getFileExtension,
   getMimeTypeFromExtension,
 } from '@/lib/uploads/utils/file-utils'
+import { workflowBorderColor } from '@/lib/workspaces/colors'
 import {
   FileViewer,
   type PreviewMode,
@@ -514,7 +515,7 @@ function EmbeddedFolder({ workspaceId, folderId }: EmbeddedFolderProps) {
                 className='h-[12px] w-[12px] flex-shrink-0 rounded-[3px] border-[2px]'
                 style={{
                   backgroundColor: w.color,
-                  borderColor: `${w.color}60`,
+                  borderColor: workflowBorderColor(w.color),
                   backgroundClip: 'padding-box',
                 }}
               />
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry/resource-registry.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry/resource-registry.tsx
index e10c31b9a61..59628a41237 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry/resource-registry.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry/resource-registry.tsx
@@ -4,6 +4,7 @@ import { type ElementType, type ReactNode, useMemo } from 'react'
 import type { QueryClient } from '@tanstack/react-query'
 import { useParams } from 'next/navigation'
 import {
+  Blimp,
   Database,
   File as FileIcon,
   Folder as FolderIcon,
@@ -13,12 +14,14 @@ import {
 import { WorkflowIcon } from '@/components/icons'
 import { getDocumentIcon } from '@/components/icons/document-icons'
 import { cn } from '@/lib/core/utils/cn'
+import { workflowBorderColor } from '@/lib/workspaces/colors'
 import type {
   MothershipResource,
   MothershipResourceType,
 } from '@/app/workspace/[workspaceId]/home/types'
 import { knowledgeKeys } from '@/hooks/queries/kb/knowledge'
 import { tableKeys } from '@/hooks/queries/tables'
+import { taskKeys } from '@/hooks/queries/tasks'
 import { folderKeys } from '@/hooks/queries/utils/folder-keys'
 import { invalidateWorkflowLists } from '@/hooks/queries/utils/invalidate-workflow-lists'
 import { useWorkflows } from '@/hooks/queries/workflows'
@@ -48,7 +51,7 @@ function WorkflowTabSquare({ workflowId, className }: { workflowId: string; clas
       className={cn('flex-shrink-0 rounded-[3px] border-[2px]', className)}
       style={{
         backgroundColor: color,
-        borderColor: `${color}60`,
+        borderColor: workflowBorderColor(color),
         backgroundClip: 'padding-box',
       }}
     />
@@ -63,7 +66,7 @@ function WorkflowDropdownItem({ item }: DropdownItemRenderProps) {
         className='h-[14px] w-[14px] flex-shrink-0 rounded-[3px] border-[2px]'
         style={{
           backgroundColor: color,
-          borderColor: `${color}60`,
+          borderColor: workflowBorderColor(color),
           backgroundClip: 'padding-box',
         }}
       />
@@ -151,6 +154,15 @@ export const RESOURCE_REGISTRY: Record ,
   },
+  task: {
+    type: 'task',
+    label: 'Tasks',
+    icon: Blimp,
+    renderTabIcon: (_resource, className) => (
+      
+    ),
+    renderDropdownItem: (props) => ,
+  },
 } as const
 
 export const RESOURCE_TYPES = Object.values(RESOURCE_REGISTRY)
@@ -185,6 +197,9 @@ const RESOURCE_INVALIDATORS: Record<
   folder: (qc) => {
     qc.invalidateQueries({ queryKey: folderKeys.lists() })
   },
+  task: (qc, wId) => {
+    qc.invalidateQueries({ queryKey: taskKeys.list(wId) })
+  },
 }
 
 /**
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-tabs/resource-tabs.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-tabs/resource-tabs.tsx
index a3ef9628d03..2809070fde2 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-tabs/resource-tabs.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-tabs/resource-tabs.tsx
@@ -10,6 +10,7 @@ import {
 import { Button, Tooltip } from '@/components/emcn'
 import { Columns3, Eye, PanelLeft, Pencil } from '@/components/emcn/icons'
 import { isEphemeralResource } from '@/lib/copilot/resource-extraction'
+import { SIM_RESOURCE_DRAG_TYPE } from '@/lib/copilot/resource-types'
 import { cn } from '@/lib/core/utils/cn'
 import type { PreviewMode } from '@/app/workspace/[workspaceId]/files/components/file-viewer'
 import { AddResourceDropdown } from '@/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown'
@@ -164,7 +165,7 @@ export function ResourceTabs({
       const resource = resources[idx]
       if (resource) {
         e.dataTransfer.setData(
-          'application/x-sim-resource',
+          SIM_RESOURCE_DRAG_TYPE,
           JSON.stringify({ type: resource.type, id: resource.id, title: resource.title })
         )
       }
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/constants.ts b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/constants.ts
index d619aed9102..8c2516d5181 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/constants.ts
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/constants.ts
@@ -89,6 +89,8 @@ export function mapResourceToContext(resource: MothershipResource): ChatContext
       return { kind: 'file', fileId: resource.id, label: resource.title }
     case 'folder':
       return { kind: 'folder', folderId: resource.id, label: resource.title }
+    case 'task':
+      return { kind: 'past_chat', chatId: resource.id, label: resource.title }
     default:
       return { kind: 'docs', label: resource.title }
   }
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/plus-menu-dropdown.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/plus-menu-dropdown.tsx
index 42882beafa0..d5656d4cf23 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/plus-menu-dropdown.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/plus-menu-dropdown.tsx
@@ -81,7 +81,7 @@ export const PlusMenuDropdown = React.memo(
           e.preventDefault()
           const firstItem = contentRef.current?.querySelector('[role="menuitem"]')
           firstItem?.focus()
-        } else if (e.key === 'Enter') {
+        } else if (e.key === 'Enter' || e.key === 'Tab') {
           e.preventDefault()
           const first = filteredItemsRef.current?.[0]
           if (first) handleSelect({ type: first.type, id: first.item.id, title: first.item.name })
@@ -99,6 +99,12 @@ export const PlusMenuDropdown = React.memo(
           e.preventDefault()
           searchRef.current?.focus()
         }
+      } else if (e.key === 'Tab') {
+        const focused = document.activeElement as HTMLElement | null
+        if (focused?.getAttribute('role') === 'menuitem') {
+          e.preventDefault()
+          focused.click()
+        }
       }
     }, [])
 
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx
index d2415318b31..dc5969881c6 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx
@@ -3,11 +3,11 @@
 import type React from 'react'
 import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
 import { useParams } from 'next/navigation'
-import { Database, Folder as FolderIcon, Table as TableIcon } from '@/components/emcn/icons'
-import { getDocumentIcon } from '@/components/icons/document-icons'
 import { useSession } from '@/lib/auth/auth-client'
+import { SIM_RESOURCE_DRAG_TYPE, SIM_RESOURCES_DRAG_TYPE } from '@/lib/copilot/resource-types'
 import { cn } from '@/lib/core/utils/cn'
 import { CHAT_ACCEPT_ATTRIBUTE } from '@/lib/uploads/utils/validation'
+import { ContextMentionIcon } from '@/app/workspace/[workspaceId]/home/components/context-mention-icon'
 import { useAvailableResources } from '@/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown'
 import type {
   PlusMenuHandle,
@@ -108,6 +108,7 @@ interface UserInputProps {
   isInitialView?: boolean
   userId?: string
   onContextAdd?: (context: ChatContext) => void
+  onContextRemove?: (context: ChatContext) => void
   onEnterWhileEmpty?: () => boolean
 }
 
@@ -121,6 +122,7 @@ export function UserInput({
   isInitialView = true,
   userId,
   onContextAdd,
+  onContextRemove,
   onEnterWhileEmpty,
 }: UserInputProps) {
   const { workspaceId } = useParams<{ workspaceId: string }>()
@@ -170,6 +172,37 @@ export function UserInput({
     [addContext, onContextAdd]
   )
 
+  const onContextRemoveRef = useRef(onContextRemove)
+  onContextRemoveRef.current = onContextRemove
+
+  const prevSelectedContextsRef = useRef([])
+  useEffect(() => {
+    const prev = prevSelectedContextsRef.current
+    const curr = contextManagement.selectedContexts
+    const contextId = (ctx: ChatContext): string => {
+      switch (ctx.kind) {
+        case 'workflow':
+        case 'current_workflow':
+          return `${ctx.kind}:${ctx.workflowId}`
+        case 'knowledge':
+          return `knowledge:${ctx.knowledgeId ?? ''}`
+        case 'table':
+          return `table:${ctx.tableId}`
+        case 'file':
+          return `file:${ctx.fileId}`
+        case 'folder':
+          return `folder:${ctx.folderId}`
+        case 'past_chat':
+          return `past_chat:${ctx.chatId}`
+        default:
+          return `${ctx.kind}:${ctx.label}`
+      }
+    }
+    const removed = prev.filter((p) => !curr.some((c) => contextId(c) === contextId(p)))
+    if (removed.length > 0) removed.forEach((ctx) => onContextRemoveRef.current?.(ctx))
+    prevSelectedContextsRef.current = curr
+  }, [contextManagement.selectedContexts])
+
   const existingResourceKeys = useMemo(() => {
     const keys = new Set()
     for (const ctx of contextManagement.selectedContexts) {
@@ -178,6 +211,7 @@ export function UserInput({
       if (ctx.kind === 'table' && ctx.tableId) keys.add(`table:${ctx.tableId}`)
       if (ctx.kind === 'file' && ctx.fileId) keys.add(`file:${ctx.fileId}`)
       if (ctx.kind === 'folder' && ctx.folderId) keys.add(`folder:${ctx.folderId}`)
+      if (ctx.kind === 'past_chat' && ctx.chatId) keys.add(`task:${ctx.chatId}`)
     }
     return keys
   }, [contextManagement.selectedContexts])
@@ -247,15 +281,17 @@ export function UserInput({
       if (textarea) {
         const currentValue = valueRef.current
         const insertAt = atInsertPosRef.current ?? textarea.selectionStart ?? currentValue.length
-        atInsertPosRef.current = null
-
         const needsSpaceBefore = insertAt > 0 && !/\s/.test(currentValue.charAt(insertAt - 1))
         const insertText = `${needsSpaceBefore ? ' ' : ''}@${resource.title} `
         const before = currentValue.slice(0, insertAt)
         const after = currentValue.slice(insertAt)
+        const newValue = `${before}${insertText}${after}`
         const newPos = before.length + insertText.length
         pendingCursorRef.current = newPos
-        setValue(`${before}${insertText}${after}`)
+        // Eagerly sync refs so successive drop-handler iterations see the updated position
+        valueRef.current = newValue
+        atInsertPosRef.current = newPos
+        setValue(newValue)
       }
 
       const context = mapResourceToContext(resource)
@@ -281,7 +317,10 @@ export function UserInput({
   }, [])
 
   const handleContainerDragOver = useCallback((e: React.DragEvent) => {
-    if (e.dataTransfer.types.includes('application/x-sim-resource')) {
+    if (
+      e.dataTransfer.types.includes(SIM_RESOURCE_DRAG_TYPE) ||
+      e.dataTransfer.types.includes(SIM_RESOURCES_DRAG_TYPE)
+    ) {
       e.preventDefault()
       e.stopPropagation()
       e.dataTransfer.dropEffect = 'copy'
@@ -292,13 +331,30 @@ export function UserInput({
 
   const handleContainerDrop = useCallback(
     (e: React.DragEvent) => {
-      const resourceJson = e.dataTransfer.getData('application/x-sim-resource')
+      const resourcesJson = e.dataTransfer.getData(SIM_RESOURCES_DRAG_TYPE)
+      if (resourcesJson) {
+        e.preventDefault()
+        e.stopPropagation()
+        try {
+          const resources = JSON.parse(resourcesJson) as MothershipResource[]
+          for (const resource of resources) {
+            handleResourceSelect(resource)
+          }
+          // Reset after batch so the next non-drop insert uses the cursor position
+          atInsertPosRef.current = null
+        } catch {
+          // Invalid JSON — ignore
+        }
+        return
+      }
+      const resourceJson = e.dataTransfer.getData(SIM_RESOURCE_DRAG_TYPE)
       if (resourceJson) {
         e.preventDefault()
         e.stopPropagation()
         try {
           const resource = JSON.parse(resourceJson) as MothershipResource
           handleResourceSelect(resource)
+          atInsertPosRef.current = null
         } catch {
           // Invalid JSON — ignore
         }
@@ -310,11 +366,17 @@ export function UserInput({
   )
 
   const handleDragEnter = useCallback((e: React.DragEvent) => {
-    filesRef.current.handleDragEnter(e)
+    const isResourceDrag =
+      e.dataTransfer.types.includes(SIM_RESOURCE_DRAG_TYPE) ||
+      e.dataTransfer.types.includes(SIM_RESOURCES_DRAG_TYPE)
+    if (!isResourceDrag) filesRef.current.handleDragEnter(e)
   }, [])
 
   const handleDragLeave = useCallback((e: React.DragEvent) => {
-    filesRef.current.handleDragLeave(e)
+    const isResourceDrag =
+      e.dataTransfer.types.includes(SIM_RESOURCE_DRAG_TYPE) ||
+      e.dataTransfer.types.includes(SIM_RESOURCES_DRAG_TYPE)
+    if (!isResourceDrag) filesRef.current.handleDragLeave(e)
   }, [])
 
   const handleFileChange = useCallback((e: React.ChangeEvent) => {
@@ -643,42 +705,17 @@ export function UserInput({
           : range.token
       const matchingCtx = contexts.find((c) => c.label === mentionLabel)
 
-      let mentionIconNode: React.ReactNode = null
-      if (matchingCtx) {
-        const iconClasses = 'absolute inset-0 m-auto h-[12px] w-[12px] text-[var(--text-icon)]'
-        switch (matchingCtx.kind) {
-          case 'workflow':
-          case 'current_workflow': {
-            const wfId = (matchingCtx as { workflowId: string }).workflowId
-            const wfColor = workflowsById[wfId]?.color ?? '#888'
-            mentionIconNode = (
-              
- ) - break - } - case 'knowledge': - mentionIconNode = - break - case 'table': - mentionIconNode = - break - case 'file': { - const FileDocIcon = getDocumentIcon('', mentionLabel) - mentionIconNode = - break - } - case 'folder': - mentionIconNode = - break - } - } + const wfId = + matchingCtx?.kind === 'workflow' || matchingCtx?.kind === 'current_workflow' + ? matchingCtx.workflowId + : undefined + const mentionIconNode = matchingCtx ? ( + + ) : null elements.push( w.id === context.workflowId)?.color ?? null }, [workflowList, context.kind, context.workflowId]) - let icon: React.ReactNode = null - const iconClasses = 'h-[12px] w-[12px] flex-shrink-0 text-[var(--text-icon)]' - - switch (context.kind) { - case 'workflow': - case 'current_workflow': - icon = workflowColor ? ( - - ) : null - break - case 'knowledge': - icon = - break - case 'table': - icon = - break - case 'file': { - const FileDocIcon = getDocumentIcon('', context.label) - icon = - break - } - case 'folder': - icon = - break - } - return ( - {icon && {icon}} + {context.label} ) diff --git a/apps/sim/app/workspace/[workspaceId]/home/home.tsx b/apps/sim/app/workspace/[workspaceId]/home/home.tsx index d76f17ff454..132d87b2a9e 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/home.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/home.tsx @@ -17,7 +17,7 @@ import { useChatHistory, useMarkTaskRead } from '@/hooks/queries/tasks' import type { ChatContext } from '@/stores/panel' import { MothershipChat, MothershipView, TemplatePrompts, UserInput } from './components' import { getMothershipUseChatOptions, useChat, useMothershipResize } from './hooks' -import type { FileAttachmentForApi, MothershipResource, MothershipResourceType } from './types' +import type { FileAttachmentForApi, MothershipResourceType } from './types' const logger = createLogger('Home') @@ -223,6 +223,14 @@ export function Home({ chatId }: HomeProps = {}) { posthogRef.current = posthog }, [posthog]) + const handleStopGeneration = useCallback(() => { + captureEvent(posthogRef.current, 'task_generation_aborted', { + workspace_id: workspaceId, + view: 'mothership', + }) + stopGeneration() + }, [stopGeneration, workspaceId]) + const handleSubmit = useCallback( (text: string, fileAttachments?: FileAttachmentForApi[], contexts?: ChatContext[]) => { const trimmed = text.trim() @@ -253,51 +261,42 @@ export function Home({ chatId }: HomeProps = {}) { return () => window.removeEventListener('mothership-send-message', handler) }, [sendMessage]) - const handleContextAdd = useCallback( - (context: ChatContext) => { - let resourceType: MothershipResourceType | null = null - let resourceId: string | null = null - const resourceTitle: string = context.label - + const resolveResourceFromContext = useCallback( + (context: ChatContext): { type: MothershipResourceType; id: string } | null => { switch (context.kind) { case 'workflow': case 'current_workflow': - resourceType = 'workflow' - resourceId = context.workflowId - break + return context.workflowId ? { type: 'workflow', id: context.workflowId } : null case 'knowledge': - if (context.knowledgeId) { - resourceType = 'knowledgebase' - resourceId = context.knowledgeId - } - break + return context.knowledgeId ? { type: 'knowledgebase', id: context.knowledgeId } : null case 'table': - if (context.tableId) { - resourceType = 'table' - resourceId = context.tableId - } - break + return context.tableId ? { type: 'table', id: context.tableId } : null case 'file': - if (context.fileId) { - resourceType = 'file' - resourceId = context.fileId - } - break + return context.fileId ? { type: 'file', id: context.fileId } : null default: - break + return null } + }, + [] + ) - if (resourceType && resourceId) { - const resource: MothershipResource = { - type: resourceType, - id: resourceId, - title: resourceTitle, - } - addResource(resource) + const handleContextAdd = useCallback( + (context: ChatContext) => { + const resolved = resolveResourceFromContext(context) + if (resolved) { + addResource({ ...resolved, title: context.label }) handleResourceEvent() } }, - [addResource, handleResourceEvent] + [resolveResourceFromContext, addResource, handleResourceEvent] + ) + + const handleContextRemove = useCallback( + (context: ChatContext) => { + const resolved = resolveResourceFromContext(context) + if (resolved) removeResource(resolved.type, resolved.id) + }, + [resolveResourceFromContext, removeResource] ) const hasMessages = messages.length > 0 @@ -334,9 +333,10 @@ export function Home({ chatId }: HomeProps = {}) { defaultValue={initialPrompt} onSubmit={handleSubmit} isSending={isSending} - onStopGeneration={stopGeneration} + onStopGeneration={handleStopGeneration} userId={session?.user?.id} onContextAdd={handleContextAdd} + onContextRemove={handleContextRemove} />
@@ -359,7 +359,7 @@ export function Home({ chatId }: HomeProps = {}) { isSending={isSending} isReconnecting={isReconnecting} onSubmit={handleSubmit} - onStopGeneration={stopGeneration} + onStopGeneration={handleStopGeneration} messageQueue={messageQueue} onRemoveQueuedMessage={removeFromQueue} onSendQueuedMessage={sendNow} @@ -367,6 +367,7 @@ export function Home({ chatId }: HomeProps = {}) { userId={session?.user?.id} chatId={resolvedChatId} onContextAdd={handleContextAdd} + onContextRemove={handleContextRemove} editValue={editingInputValue} onEditValueConsumed={clearEditingValue} animateInput={isInputEntering} diff --git a/apps/sim/app/workspace/[workspaceId]/home/types.ts b/apps/sim/app/workspace/[workspaceId]/home/types.ts index d4f812cc25a..e6ae3c9f0a9 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/types.ts +++ b/apps/sim/app/workspace/[workspaceId]/home/types.ts @@ -6,6 +6,9 @@ export type { MothershipResourceType, } from '@/lib/copilot/resource-types' +/** Union of all valid context kind strings, derived from {@link ChatContext}. */ +export type ChatContextKind = ChatContext['kind'] + export interface FileAttachmentForApi { id: string key: string @@ -260,13 +263,14 @@ export interface ChatMessageAttachment { } export interface ChatMessageContext { - kind: string + kind: ChatContextKind label: string workflowId?: string knowledgeId?: string tableId?: string fileId?: string folderId?: string + chatId?: string } export interface ChatMessage { diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/connectors-section/connectors-section.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/connectors-section/connectors-section.tsx index 578b6df254e..3d08cdb7c62 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/connectors-section/connectors-section.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/connectors-section/connectors-section.tsx @@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { createLogger } from '@sim/logger' -import { format, formatDistanceToNow } from 'date-fns' +import { format, formatDistanceToNow, isPast } from 'date-fns' import { AlertCircle, CheckCircle2, @@ -380,7 +380,9 @@ function ConnectorCard({ · Next sync:{' '} - {formatDistanceToNow(new Date(connector.nextSyncAt), { addSuffix: true })} + {isPast(new Date(connector.nextSyncAt)) + ? 'pending' + : formatDistanceToNow(new Date(connector.nextSyncAt), { addSuffix: true })} )} diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/dashboard/components/workflows-list/workflows-list.tsx b/apps/sim/app/workspace/[workspaceId]/logs/components/dashboard/components/workflows-list/workflows-list.tsx index ee52e0fa0bc..2f7b7f38cd9 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/dashboard/components/workflows-list/workflows-list.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/dashboard/components/workflows-list/workflows-list.tsx @@ -1,6 +1,7 @@ import { memo } from 'react' import { useParams } from 'next/navigation' import { cn } from '@/lib/core/utils/cn' +import { workflowBorderColor } from '@/lib/workspaces/colors' import { DELETED_WORKFLOW_COLOR, DELETED_WORKFLOW_LABEL, @@ -93,7 +94,7 @@ function WorkflowsListInner({ className='h-[10px] w-[10px] flex-shrink-0 rounded-[3px] border-[1.5px]' style={{ backgroundColor: workflowColor, - borderColor: `${workflowColor}60`, + borderColor: workflowBorderColor(workflowColor), backgroundClip: 'padding-box', }} /> diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/log-details.tsx b/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/log-details.tsx index 0289e4d9280..4307d40b5c5 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/log-details.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/log-details.tsx @@ -20,6 +20,7 @@ import { BASE_EXECUTION_CHARGE } from '@/lib/billing/constants' import { cn } from '@/lib/core/utils/cn' import { formatDuration } from '@/lib/core/utils/formatting' import { filterHiddenOutputKeys } from '@/lib/logs/execution/trace-spans/trace-spans' +import { workflowBorderColor } from '@/lib/workspaces/colors' import { ExecutionSnapshot, FileCards, @@ -431,7 +432,7 @@ export const LogDetails = memo(function LogDetails({ className='h-[10px] w-[10px] flex-shrink-0 rounded-[3px] border-[1.5px]' style={{ backgroundColor: c, - borderColor: c ? `${c}60` : undefined, + borderColor: c ? workflowBorderColor(c) : undefined, backgroundClip: 'padding-box', }} /> diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/logs-list/logs-list.tsx b/apps/sim/app/workspace/[workspaceId]/logs/components/logs-list/logs-list.tsx index c99a59988fa..3d1aba93508 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/logs-list/logs-list.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/logs-list/logs-list.tsx @@ -8,6 +8,7 @@ import { Badge, buttonVariants } from '@/components/emcn' import { dollarsToCredits } from '@/lib/billing/credits/conversion' import { cn } from '@/lib/core/utils/cn' import { formatDuration } from '@/lib/core/utils/formatting' +import { workflowBorderColor } from '@/lib/workspaces/colors' import { DELETED_WORKFLOW_COLOR, DELETED_WORKFLOW_LABEL, @@ -90,7 +91,7 @@ const LogRow = memo( className='h-[10px] w-[10px] flex-shrink-0 rounded-[3px] border-[1.5px]' style={{ backgroundColor: workflowColor, - borderColor: `${workflowColor}60`, + borderColor: workflowBorderColor(workflowColor), backgroundClip: 'padding-box', }} /> diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/logs-toolbar.tsx b/apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/logs-toolbar.tsx index bcaf5d3019a..518ad67654b 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/logs-toolbar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/logs-toolbar.tsx @@ -20,6 +20,7 @@ import { cn } from '@/lib/core/utils/cn' import { hasActiveFilters } from '@/lib/logs/filters' import { getTriggerOptions } from '@/lib/logs/get-trigger-options' import { captureEvent } from '@/lib/posthog/client' +import { workflowBorderColor } from '@/lib/workspaces/colors' import { type LogStatus, STATUS_CONFIG } from '@/app/workspace/[workspaceId]/logs/utils' import { getBlock } from '@/blocks/registry' import { useFolderMap } from '@/hooks/queries/folders' @@ -124,7 +125,7 @@ function getColorIcon( width: 10, height: 10, ...(withRing && { - borderColor: `${color}60`, + borderColor: workflowBorderColor(color), backgroundClip: 'padding-box' as const, }), }} @@ -604,7 +605,7 @@ export const LogsToolbar = memo(function LogsToolbar({ className='h-[8px] w-[8px] flex-shrink-0 rounded-xs border-[1.5px]' style={{ backgroundColor: selectedWorkflow.color, - borderColor: `${selectedWorkflow.color}60`, + borderColor: workflowBorderColor(selectedWorkflow.color), backgroundClip: 'padding-box', }} /> @@ -735,7 +736,7 @@ export const LogsToolbar = memo(function LogsToolbar({ className='h-[8px] w-[8px] flex-shrink-0 rounded-xs border-[1.5px]' style={{ backgroundColor: selectedWorkflow.color, - borderColor: `${selectedWorkflow.color}60`, + borderColor: workflowBorderColor(selectedWorkflow.color), backgroundClip: 'padding-box', }} /> diff --git a/apps/sim/app/workspace/[workspaceId]/logs/logs.tsx b/apps/sim/app/workspace/[workspaceId]/logs/logs.tsx index a939a859c1b..f8708263c76 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/logs.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/logs.tsx @@ -33,6 +33,7 @@ import { type TriggerData, type WorkflowData, } from '@/lib/logs/search-suggestions' +import { workflowBorderColor } from '@/lib/workspaces/colors' import type { FilterTag, HeaderAction, @@ -157,7 +158,7 @@ function getColorIcon( width: 10, height: 10, ...(withRing && { - borderColor: `${color}60`, + borderColor: workflowBorderColor(color), backgroundClip: 'padding-box' as const, }), }} @@ -742,7 +743,7 @@ export default function Logs() { className='h-[10px] w-[10px] rounded-[3px] border-[1.5px]' style={{ backgroundColor: workflowColor, - borderColor: `${workflowColor}60`, + borderColor: workflowBorderColor(workflowColor), backgroundClip: 'padding-box', }} /> @@ -1441,7 +1442,7 @@ function LogsFilterPanel({ searchQuery, onSearchQueryChange }: LogsFilterPanelPr className='h-[8px] w-[8px] flex-shrink-0 rounded-xs border-[1.5px]' style={{ backgroundColor: selectedWorkflow.color, - borderColor: `${selectedWorkflow.color}60`, + borderColor: workflowBorderColor(selectedWorkflow.color), backgroundClip: 'padding-box', }} /> diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/admin/admin.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/admin/admin.tsx index 5105cb404f3..f316f1f1294 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/admin/admin.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/admin/admin.tsx @@ -227,123 +227,128 @@ export function Admin() {
- - {u.name || '—'} - - {u.email} - - {u.role || 'user'} - - - {u.banned ? ( - Banned - ) : ( - Active - )} - - - {u.id !== session?.user?.id && ( - <> - - - {u.banned ? ( +
+ + {u.name || '—'} + + {u.email} + + + {u.role || 'user'} + + + + {u.banned ? ( + Banned + ) : ( + Active + )} + + + {u.id !== session?.user?.id && ( + <> + - ) : banUserId === u.id ? ( -
- setBanReason(e.target.value)} - placeholder='Reason (optional)' - className='h-[28px] w-[120px] text-caption' - /> + {u.banned ? ( + ) : ( -
- ) : ( - - )} - - )} -
+ )} + + )} + +
+ {banUserId === u.id && !u.banned && ( +
+ setBanReason(e.target.value)} + placeholder='Reason (optional)' + className='h-[28px] flex-1 text-caption' + /> + +
+ )}
))}
diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/credentials/credentials-manager.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/credentials/credentials-manager.tsx index d3916265d72..cf2f07519d1 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/credentials/credentials-manager.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/credentials/credentials-manager.tsx @@ -2,6 +2,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { createLogger } from '@sim/logger' +import { useQueryClient } from '@tanstack/react-query' import { Check, Clipboard, Key, Search } from 'lucide-react' import { useParams, useRouter } from 'next/navigation' import { @@ -42,6 +43,7 @@ import { useWorkspaceCredentials, type WorkspaceCredential, type WorkspaceCredentialRole, + workspaceCredentialKeys, } from '@/hooks/queries/credentials' import { usePersonalEnvironment, @@ -125,9 +127,11 @@ interface WorkspaceVariableRowProps { renamingKey: string | null pendingKeyValue: string hasCredential: boolean + canEdit: boolean onRenameStart: (key: string) => void onPendingKeyChange: (value: string) => void onRenameEnd: (key: string, value: string) => void + onValueChange: (key: string, value: string) => void onDelete: (key: string) => void onViewDetails: (envKey: string) => void } @@ -138,12 +142,16 @@ function WorkspaceVariableRow({ renamingKey, pendingKeyValue, hasCredential, + canEdit, onRenameStart, onPendingKeyChange, onRenameEnd, + onValueChange, onDelete, onViewDetails, }: WorkspaceVariableRowProps) { + const [valueFocused, setValueFocused] = useState(false) + return (
e.target.removeAttribute('readOnly')} + onFocus={(e) => { + if (canEdit) e.target.removeAttribute('readOnly') + }} className='h-9' />
onValueChange(envKey, e.target.value)} readOnly + onFocus={(e) => { + if (canEdit) { + setValueFocused(true) + e.target.removeAttribute('readOnly') + } + }} + onBlur={() => { + if (canEdit) setValueFocused(false) + }} + name={`workspace_env_value_${envKey}_${Math.random()}`} autoComplete='off' autoCorrect='off' autoCapitalize='off' @@ -179,14 +201,18 @@ function WorkspaceVariableRow({ > Details - - - - - Delete secret - + {canEdit ? ( + + + + + Delete secret + + ) : ( +
+ )}
) } @@ -298,6 +324,14 @@ export function CredentialsManager() { ) const { data: workspacePermissions } = useWorkspacePermissionsQuery(workspaceId || null) + const queryClient = useQueryClient() + + const isWorkspaceAdmin = useMemo(() => { + const userId = session?.user?.id + if (!userId || !workspacePermissions?.users) return false + const currentUser = workspacePermissions.users.find((user) => user.userId === userId) + return currentUser?.permissionType === 'admin' + }, [session?.user?.id, workspacePermissions?.users]) const isLoading = isPersonalLoading || isWorkspaceLoading const variables = useMemo(() => personalEnvData || {}, [personalEnvData]) @@ -767,6 +801,10 @@ export function CredentialsManager() { [pendingKeyValue, renamingKey] ) + const handleWorkspaceValueChange = useCallback((key: string, value: string) => { + setWorkspaceVars((prev) => ({ ...prev, [key]: value })) + }, []) + const handleDeleteWorkspaceVar = useCallback((key: string) => { setWorkspaceVars((prev) => { const next = { ...prev } @@ -923,6 +961,7 @@ export function CredentialsManager() { const prevInitialVars = [...initialVarsRef.current] const prevInitialWorkspaceVars = { ...initialWorkspaceVarsRef.current } + const mutations: Promise[] = [] try { setShowUnsavedChanges(false) @@ -944,8 +983,6 @@ export function CredentialsManager() { .filter((v) => v.key && v.value) .reduce>((acc, { key, value }) => ({ ...acc, [key]: value }), {}) - await savePersonalMutation.mutateAsync({ variables: validVariables }) - const before = prevInitialWorkspaceVars const after = mergedWorkspaceVars const toUpsert: Record = {} @@ -961,14 +998,37 @@ export function CredentialsManager() { if (!(k in after)) toDelete.push(k) } - if (workspaceId) { - if (Object.keys(toUpsert).length) { - await upsertWorkspaceMutation.mutateAsync({ workspaceId, variables: toUpsert }) - } - if (toDelete.length) { - await removeWorkspaceMutation.mutateAsync({ workspaceId, keys: toDelete }) + const personalChanged = (() => { + const initialMap = new Map( + prevInitialVars.filter((v) => v.key && v.value).map((v) => [v.key, v.value]) + ) + const currentKeys = Object.keys(validVariables) + if (initialMap.size !== currentKeys.length) return true + for (const [key, value] of Object.entries(validVariables)) { + if (initialMap.get(key) !== value) return true } + return false + })() + + if (personalChanged) { + mutations.push(savePersonalMutation.mutateAsync({ variables: validVariables })) } + if (workspaceId && (Object.keys(toUpsert).length || toDelete.length)) { + mutations.push( + (async () => { + if (Object.keys(toUpsert).length) { + await upsertWorkspaceMutation.mutateAsync({ workspaceId, variables: toUpsert }) + } + if (toDelete.length) { + await removeWorkspaceMutation.mutateAsync({ workspaceId, keys: toDelete }) + } + })() + ) + } + + const results = await Promise.allSettled(mutations) + const firstFailure = results.find((r): r is PromiseRejectedResult => r.status === 'rejected') + if (firstFailure) throw firstFailure.reason setWorkspaceVars(mergedWorkspaceVars) setNewWorkspaceRows([createEmptyEnvVar()]) @@ -977,17 +1037,13 @@ export function CredentialsManager() { initialVarsRef.current = prevInitialVars initialWorkspaceVarsRef.current = prevInitialWorkspaceVars logger.error('Failed to save environment variables:', error) + } finally { + if (mutations.length > 0) { + queryClient.invalidateQueries({ queryKey: workspaceCredentialKeys.lists() }) + } } - }, [ - isListSaving, - envVars, - workspaceVars, - newWorkspaceRows, - workspaceId, - savePersonalMutation, - upsertWorkspaceMutation, - removeWorkspaceMutation, - ]) + // eslint-disable-next-line react-hooks/exhaustive-deps -- mutation objects and queryClient are stable (TanStack Query v5) + }, [isListSaving, envVars, workspaceVars, newWorkspaceRows, workspaceId]) const handleDiscardAndNavigate = useCallback(() => { shouldBlockNavRef.current = false @@ -1494,24 +1550,27 @@ export function CredentialsManager() { renamingKey={renamingKey} pendingKeyValue={pendingKeyValue} hasCredential={envKeyToCredential.has(key)} + canEdit={isWorkspaceAdmin} onRenameStart={setRenamingKey} onPendingKeyChange={setPendingKeyValue} onRenameEnd={handleWorkspaceKeyRename} + onValueChange={handleWorkspaceValueChange} onDelete={handleDeleteWorkspaceVar} onViewDetails={(envKey) => handleViewDetails(envKey, 'env_workspace')} /> ))} - {(searchTerm.trim() - ? filteredNewWorkspaceRows - : newWorkspaceRows.map((row, index) => ({ row, originalIndex: index })) - ).map(({ row, originalIndex }) => ( - - ))} + {isWorkspaceAdmin && + (searchTerm.trim() + ? filteredNewWorkspaceRows + : newWorkspaceRows.map((row, index) => ({ row, originalIndex: index })) + ).map(({ row, originalIndex }) => ( + + ))}
)} diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/recently-deleted/recently-deleted.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/recently-deleted/recently-deleted.tsx index 3cd6d051ca7..48c5dbbc5fd 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/recently-deleted/recently-deleted.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/recently-deleted/recently-deleted.tsx @@ -6,6 +6,7 @@ import { useParams, useRouter } from 'next/navigation' import { Button, Combobox, SModalTabs, SModalTabsList, SModalTabsTrigger } from '@/components/emcn' import { Input } from '@/components/ui' import { formatDate } from '@/lib/core/utils/formatting' +import { workflowBorderColor } from '@/lib/workspaces/colors' import { RESOURCE_REGISTRY } from '@/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry' import type { MothershipResourceType } from '@/app/workspace/[workspaceId]/home/types' import { DeletedItemSkeleton } from '@/app/workspace/[workspaceId]/settings/components/recently-deleted/deleted-item-skeleton' @@ -97,7 +98,7 @@ function ResourceIcon({ resource }: { resource: DeletedResource }) { className='h-[14px] w-[14px] shrink-0 rounded-[3px] border-[2px]' style={{ backgroundColor: color, - borderColor: `${color}60`, + borderColor: workflowBorderColor(color), backgroundClip: 'padding-box', }} /> diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx index 4d485c763ce..da51910789b 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx @@ -4,6 +4,7 @@ import { memo, useCallback, useEffect, useRef, useState } from 'react' import { createLogger } from '@sim/logger' import { History, Plus, Square } from 'lucide-react' import { useParams, useRouter } from 'next/navigation' +import { usePostHog } from 'posthog-js/react' import { useShallow } from 'zustand/react/shallow' import { BubbleChatClose, @@ -33,6 +34,7 @@ import { import { Lock, Unlock, Upload } from '@/components/emcn/icons' import { VariableIcon } from '@/components/icons' import { useSession } from '@/lib/auth/auth-client' +import { captureEvent } from '@/lib/posthog/client' import { generateWorkflowJson } from '@/lib/workflows/operations/import-export' import { ConversationListItem } from '@/app/workspace/[workspaceId]/components' import { MothershipChat } from '@/app/workspace/[workspaceId]/home/components' @@ -101,6 +103,9 @@ export const Panel = memo(function Panel({ workspaceId: propWorkspaceId }: Panel const params = useParams() const workspaceId = propWorkspaceId ?? (params.workspaceId as string) + const posthog = usePostHog() + const posthogRef = useRef(posthog) + const panelRef = useRef(null) const fileInputRef = useRef(null) const { activeTab, setActiveTab, panelWidth, _hasHydrated, setHasHydrated } = usePanelStore( @@ -264,6 +269,10 @@ export const Panel = memo(function Panel({ workspaceId: propWorkspaceId }: Panel loadCopilotChats() }, [loadCopilotChats]) + useEffect(() => { + posthogRef.current = posthog + }, [posthog]) + const handleCopilotSelectChat = useCallback((chat: { id: string; title: string | null }) => { setCopilotChatId(chat.id) setCopilotChatTitle(chat.title) @@ -394,6 +403,14 @@ export const Panel = memo(function Panel({ workspaceId: propWorkspaceId }: Panel [copilotEditQueuedMessage] ) + const handleCopilotStopGeneration = useCallback(() => { + captureEvent(posthogRef.current, 'task_generation_aborted', { + workspace_id: workspaceId, + view: 'copilot', + }) + copilotStopGeneration() + }, [copilotStopGeneration, workspaceId]) + const handleCopilotSubmit = useCallback( (text: string, fileAttachments?: FileAttachmentForApi[], contexts?: ChatContext[]) => { const trimmed = text.trim() @@ -833,7 +850,7 @@ export const Panel = memo(function Panel({ workspaceId: propWorkspaceId }: Panel isSending={copilotIsSending} isReconnecting={copilotIsReconnecting} onSubmit={handleCopilotSubmit} - onStopGeneration={copilotStopGeneration} + onStopGeneration={handleCopilotStopGeneration} messageQueue={copilotMessageQueue} onRemoveQueuedMessage={copilotRemoveFromQueue} onSendQueuedMessage={copilotSendNow} diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/collapsed-sidebar-menu/collapsed-sidebar-menu.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/collapsed-sidebar-menu/collapsed-sidebar-menu.tsx index 7a64c8e0392..e5f869a8057 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/collapsed-sidebar-menu/collapsed-sidebar-menu.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/collapsed-sidebar-menu/collapsed-sidebar-menu.tsx @@ -14,6 +14,7 @@ import { } from '@/components/emcn' import { Pencil, SquareArrowUpRight } from '@/components/emcn/icons' import { cn } from '@/lib/core/utils/cn' +import { workflowBorderColor } from '@/lib/workspaces/colors' import { ConversationListItem } from '@/app/workspace/[workspaceId]/components' import type { useHoverMenu } from '@/app/workspace/[workspaceId]/w/components/sidebar/hooks' import type { FolderTreeNode } from '@/stores/folders/types' @@ -131,7 +132,7 @@ function WorkflowColorSwatch({ color }: { color: string }) { className='h-[16px] w-[16px] flex-shrink-0 rounded-sm border-[2.5px]' style={{ backgroundColor: color, - borderColor: `${color}60`, + borderColor: workflowBorderColor(color), backgroundClip: 'padding-box', }} /> diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/components/command-items.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/components/command-items.tsx index 7cdffe5bd57..d5c81dcf55a 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/components/command-items.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/components/command-items.tsx @@ -5,6 +5,7 @@ import { memo } from 'react' import { Command } from 'cmdk' import { Blimp } from '@/components/emcn' import { cn } from '@/lib/core/utils/cn' +import { workflowBorderColor } from '@/lib/workspaces/colors' import type { CommandItemProps } from '../utils' import { COMMAND_ITEM_CLASSNAME } from '../utils' @@ -64,7 +65,7 @@ export const MemoizedWorkflowItem = memo( className='h-[14px] w-[14px] flex-shrink-0 rounded-sm border-[2px]' style={{ backgroundColor: color, - borderColor: `${color}60`, + borderColor: workflowBorderColor(color), backgroundClip: 'padding-box', }} /> diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/folder-item/folder-item.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/folder-item/folder-item.tsx index afae818c6ac..d179568c316 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/folder-item/folder-item.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/folder-item/folder-item.tsx @@ -5,6 +5,7 @@ import { createLogger } from '@sim/logger' import clsx from 'clsx' import { ChevronRight, Folder, FolderOpen, MoreHorizontal } from 'lucide-react' import { useParams, useRouter } from 'next/navigation' +import { SIM_RESOURCES_DRAG_TYPE } from '@/lib/copilot/resource-types' import { generateId } from '@/lib/core/utils/uuid' import { getNextWorkflowColor } from '@/lib/workflows/colors' import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider' @@ -18,6 +19,10 @@ import { useSidebarDragContext, } from '@/app/workspace/[workspaceId]/w/components/sidebar/hooks' import { SIDEBAR_SCROLL_EVENT } from '@/app/workspace/[workspaceId]/w/components/sidebar/sidebar' +import { + buildDragResources, + createSidebarDragGhost, +} from '@/app/workspace/[workspaceId]/w/components/sidebar/utils' import { useCanDelete, useDeleteFolder, @@ -136,6 +141,7 @@ export function FolderItem({ }) const isEditingRef = useRef(false) + const dragGhostRef = useRef(null) const handleCreateWorkflowInFolder = useCallback(() => { const name = generateCreativeWorkflowName() @@ -196,10 +202,24 @@ export function FolderItem({ } e.dataTransfer.setData('sidebar-selection', JSON.stringify(selection)) - e.dataTransfer.effectAllowed = 'move' + e.dataTransfer.effectAllowed = 'copyMove' + + const resources = buildDragResources(selection, workspaceId) + if (resources.length > 0) { + e.dataTransfer.setData(SIM_RESOURCES_DRAG_TYPE, JSON.stringify(resources)) + } + + const total = selection.folderIds.length + selection.workflowIds.length + const ghostLabel = total > 1 ? `${folder.name} +${total - 1} more` : folder.name + const icon = total === 1 ? { kind: 'folder' as const } : undefined + const ghost = createSidebarDragGhost(ghostLabel, icon) + void ghost.offsetHeight + e.dataTransfer.setDragImage(ghost, ghost.offsetWidth / 2, ghost.offsetHeight / 2) + dragGhostRef.current = ghost + onDragStartProp?.() }, - [folder.id, onDragStartProp] + [folder.id, folder.name, workspaceId, onDragStartProp] ) const { @@ -212,6 +232,10 @@ export function FolderItem({ }) const handleDragEnd = useCallback(() => { + if (dragGhostRef.current) { + dragGhostRef.current.remove() + dragGhostRef.current = null + } handleDragEndBase() onDragEndProp?.() }, [handleDragEndBase, onDragEndProp]) diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/workflow-item/workflow-item.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/workflow-item/workflow-item.tsx index 32a58343029..3727779a43b 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/workflow-item/workflow-item.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/workflow-item/workflow-item.tsx @@ -5,6 +5,8 @@ import clsx from 'clsx' import { MoreHorizontal } from 'lucide-react' import Link from 'next/link' import { useParams } from 'next/navigation' +import { SIM_RESOURCES_DRAG_TYPE } from '@/lib/copilot/resource-types' +import { workflowBorderColor } from '@/lib/workspaces/colors' import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider' import { getWorkflowLockToggleIds } from '@/app/workspace/[workspaceId]/w/[workflowId]/utils' import { ContextMenu } from '@/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/context-menu/context-menu' @@ -16,6 +18,10 @@ import { useItemRename, useSidebarDragContext, } from '@/app/workspace/[workspaceId]/w/components/sidebar/hooks' +import { + buildDragResources, + createSidebarDragGhost, +} from '@/app/workspace/[workspaceId]/w/components/sidebar/utils' import { useCanDelete, useDeleteSelection, @@ -198,6 +204,7 @@ export function WorkflowItem({ }, [isActiveWorkflow, isWorkflowLocked]) const isEditingRef = useRef(false) + const dragGhostRef = useRef(null) const { isOpen: isContextMenuOpen, @@ -337,10 +344,25 @@ export function WorkflowItem({ } e.dataTransfer.setData('sidebar-selection', JSON.stringify(selection)) - e.dataTransfer.effectAllowed = 'move' + e.dataTransfer.effectAllowed = 'copyMove' + + const resources = buildDragResources(selection, workspaceId) + if (resources.length > 0) { + e.dataTransfer.setData(SIM_RESOURCES_DRAG_TYPE, JSON.stringify(resources)) + } + + const total = selection.workflowIds.length + selection.folderIds.length + const ghostLabel = total > 1 ? `${workflow.name} +${total - 1} more` : workflow.name + const icon = total === 1 ? { kind: 'workflow' as const, color: workflow.color } : undefined + const ghost = createSidebarDragGhost(ghostLabel, icon) + // Force reflow so the browser can capture the rendered element + void ghost.offsetHeight + e.dataTransfer.setDragImage(ghost, ghost.offsetWidth / 2, ghost.offsetHeight / 2) + dragGhostRef.current = ghost + onDragStartProp?.() }, - [workflow.id, onDragStartProp] + [workflow.id, workflow.name, workflow.color, workspaceId, onDragStartProp] ) const { @@ -353,6 +375,10 @@ export function WorkflowItem({ }) const handleDragEnd = useCallback(() => { + if (dragGhostRef.current) { + dragGhostRef.current.remove() + dragGhostRef.current = null + } handleDragEndBase() onDragEndProp?.() }, [handleDragEndBase, onDragEndProp]) @@ -414,7 +440,7 @@ export function WorkflowItem({ className='h-[16px] w-[16px] flex-shrink-0 rounded-sm border-[2.5px]' style={{ backgroundColor: workflow.color, - borderColor: `${workflow.color}60`, + borderColor: workflowBorderColor(workflow.color), backgroundClip: 'padding-box', }} /> diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx index 02f0bca42e8..1a9dcb4765c 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx @@ -37,6 +37,7 @@ import { Wordmark, } from '@/components/emcn/icons' import { useSession } from '@/lib/auth/auth-client' +import { SIM_RESOURCES_DRAG_TYPE } from '@/lib/copilot/resource-types' import { cn } from '@/lib/core/utils/cn' import { isMacPlatform } from '@/lib/core/utils/platform' import { buildFolderTree } from '@/lib/folders/tree' @@ -72,7 +73,10 @@ import { useWorkflowOperations, useWorkspaceManagement, } from '@/app/workspace/[workspaceId]/w/components/sidebar/hooks' -import { groupWorkflowsByFolder } from '@/app/workspace/[workspaceId]/w/components/sidebar/utils' +import { + createSidebarDragGhost, + groupWorkflowsByFolder, +} from '@/app/workspace/[workspaceId]/w/components/sidebar/utils' import { useDuplicateWorkspace, useExportWorkspace, @@ -159,6 +163,30 @@ const SidebarTaskItem = memo(function SidebarTaskItem({ onMorePointerDown: () => void onMoreClick: (e: React.MouseEvent, taskId: string) => void }) { + const dragGhostRef = useRef(null) + + const handleDragStart = useCallback( + (e: React.DragEvent) => { + e.dataTransfer.effectAllowed = 'copyMove' + e.dataTransfer.setData( + SIM_RESOURCES_DRAG_TYPE, + JSON.stringify([{ type: 'task', id: task.id, title: task.name }]) + ) + const ghost = createSidebarDragGhost(task.name, { kind: 'task' }) + void ghost.offsetHeight + e.dataTransfer.setDragImage(ghost, ghost.offsetWidth / 2, ghost.offsetHeight / 2) + dragGhostRef.current = ghost + }, + [task.id, task.name] + ) + + const handleDragEnd = useCallback(() => { + if (dragGhostRef.current) { + dragGhostRef.current.remove() + dragGhostRef.current = null + } + }, []) + return ( onContextMenu(e, task.id) : undefined} + draggable={task.id !== 'new'} + onDragStart={task.id !== 'new' ? handleDragStart : undefined} + onDragEnd={task.id !== 'new' ? handleDragEnd : undefined} >
{task.name}
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/utils.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/utils.ts index ecabf89b452..848f0771cb0 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/utils.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/utils.ts @@ -1,5 +1,96 @@ +import type { MothershipResource } from '@/lib/copilot/resource-types' +import { workflowBorderColor } from '@/lib/workspaces/colors' +import { getFolderMap } from '@/hooks/queries/utils/folder-cache' +import { getWorkflows } from '@/hooks/queries/utils/workflow-cache' import type { WorkflowMetadata } from '@/stores/workflows/registry/types' +/** + * Builds a `MothershipResource` array from a sidebar drag selection so it can + * be set as `application/x-sim-resources` drag data and dropped into the chat. + */ +export function buildDragResources( + selection: { workflowIds: string[]; folderIds: string[] }, + workspaceId: string +): MothershipResource[] { + const allWorkflows = getWorkflows(workspaceId) + const workflowMap = Object.fromEntries(allWorkflows.map((w) => [w.id, w])) + const folderMap = getFolderMap(workspaceId) + return [ + ...selection.workflowIds.map((id) => ({ + type: 'workflow' as const, + id, + title: workflowMap[id]?.name ?? id, + })), + ...selection.folderIds.map((id) => ({ + type: 'folder' as const, + id, + title: folderMap[id]?.name ?? id, + })), + ] +} + +export type SidebarDragGhostIcon = + | { kind: 'workflow'; color: string } + | { kind: 'folder' } + | { kind: 'task' } + +const FOLDER_SVG = `` + +const BLIMP_SVG = `` + +/** + * Creates a lightweight drag ghost pill showing an icon and label for the item(s) being dragged. + * Append to `document.body`, pass to `e.dataTransfer.setDragImage`, then remove on dragend. + */ +export function createSidebarDragGhost(label: string, icon?: SidebarDragGhostIcon): HTMLElement { + const ghost = document.createElement('div') + ghost.style.cssText = ` + position: fixed; + top: -500px; + left: 0; + display: inline-flex; + align-items: center; + gap: 6px; + padding: 4px 10px; + background: var(--surface-active); + border: 1px solid rgba(255,255,255,0.08); + border-radius: 8px; + font-family: system-ui, -apple-system, sans-serif; + font-size: 13px; + color: var(--text-body); + white-space: nowrap; + pointer-events: none; + box-shadow: 0 4px 12px rgba(0,0,0,0.4); + z-index: 9999; + ` + + if (icon) { + if (icon.kind === 'workflow') { + const square = document.createElement('div') + square.style.cssText = ` + width: 14px; height: 14px; flex-shrink: 0; + border-radius: 3px; border: 2px solid ${workflowBorderColor(icon.color)}; + background: ${icon.color}; background-clip: padding-box; + ` + ghost.appendChild(square) + } else { + const iconWrapper = document.createElement('div') + iconWrapper.style.cssText = + 'display: flex; align-items: center; flex-shrink: 0; color: var(--text-icon);' + iconWrapper.innerHTML = icon.kind === 'folder' ? FOLDER_SVG : BLIMP_SVG + ghost.appendChild(iconWrapper) + } + } + + const text = document.createElement('span') + text.style.cssText = 'max-width: 200px; overflow: hidden; text-overflow: ellipsis;' + text.textContent = label + ghost.appendChild(text) + + document.body.appendChild(ghost) + return ghost +} + export function compareByOrder( a: T, b: T diff --git a/apps/sim/app/workspace/providers/socket-provider.tsx b/apps/sim/app/workspace/providers/socket-provider.tsx index d04cc6e0fe2..b2257280c10 100644 --- a/apps/sim/app/workspace/providers/socket-provider.tsx +++ b/apps/sim/app/workspace/providers/socket-provider.tsx @@ -166,7 +166,6 @@ export function SocketProvider({ children, user }: SocketProviderProps) { const positionUpdateTimeouts = useRef>(new Map()) const isRejoiningRef = useRef(false) const pendingPositionUpdates = useRef>(new Map()) - const deletedWorkflowIdRef = useRef(null) const generateSocketToken = async (): Promise => { const res = await fetch('/api/auth/socket-token', { @@ -372,7 +371,6 @@ export function SocketProvider({ children, user }: SocketProviderProps) { socketInstance.on('workflow-deleted', (data) => { logger.warn(`Workflow ${data.workflowId} has been deleted`) - deletedWorkflowIdRef.current = data.workflowId setCurrentWorkflowId((current) => { if (current === data.workflowId) { setPresenceUsers([]) @@ -502,11 +500,7 @@ export function SocketProvider({ children, user }: SocketProviderProps) { if (error?.type === 'SESSION_ERROR') { const workflowId = urlWorkflowIdRef.current - if ( - workflowId && - !isRejoiningRef.current && - deletedWorkflowIdRef.current !== workflowId - ) { + if (workflowId && !isRejoiningRef.current) { isRejoiningRef.current = true logger.info(`Session expired, rejoining workflow: ${workflowId}`) socketInstance.emit('join-workflow', { @@ -558,25 +552,13 @@ export function SocketProvider({ children, user }: SocketProviderProps) { const hydrationPhase = useWorkflowRegistryStore((s) => s.hydration.phase) useEffect(() => { - if (!socket || !isConnected || !urlWorkflowId) { - if (!urlWorkflowId) { - deletedWorkflowIdRef.current = null - } - return - } + if (!socket || !isConnected || !urlWorkflowId) return if (hydrationPhase === 'creating') return // Skip if already in the correct room if (currentWorkflowId === urlWorkflowId) return - // Prevent rejoining a workflow that was just deleted. The URL param may - // still reference the old workflow while router.push() propagates. - if (deletedWorkflowIdRef.current === urlWorkflowId) { - return - } - deletedWorkflowIdRef.current = null - logger.info( `URL workflow changed from ${currentWorkflowId} to ${urlWorkflowId}, switching rooms` ) diff --git a/apps/sim/blocks/blocks/athena.ts b/apps/sim/blocks/blocks/athena.ts new file mode 100644 index 00000000000..8952586db61 --- /dev/null +++ b/apps/sim/blocks/blocks/athena.ts @@ -0,0 +1,469 @@ +import { AthenaIcon } from '@/components/icons' +import type { BlockConfig } from '@/blocks/types' +import { IntegrationType } from '@/blocks/types' +import type { + AthenaCreateNamedQueryResponse, + AthenaGetNamedQueryResponse, + AthenaGetQueryExecutionResponse, + AthenaGetQueryResultsResponse, + AthenaListNamedQueriesResponse, + AthenaListQueryExecutionsResponse, + AthenaStartQueryResponse, + AthenaStopQueryResponse, +} from '@/tools/athena/types' + +export const AthenaBlock: BlockConfig< + | AthenaStartQueryResponse + | AthenaGetQueryExecutionResponse + | AthenaGetQueryResultsResponse + | AthenaStopQueryResponse + | AthenaListQueryExecutionsResponse + | AthenaCreateNamedQueryResponse + | AthenaGetNamedQueryResponse + | AthenaListNamedQueriesResponse +> = { + type: 'athena', + name: 'Athena', + description: 'Run SQL queries on data in Amazon S3 using AWS Athena', + longDescription: + 'Integrate AWS Athena into workflows. Execute SQL queries against data in S3, check query status, retrieve results, manage named queries, and list executions. Requires AWS access key and secret access key.', + docsLink: 'https://docs.sim.ai/tools/athena', + category: 'tools', + integrationType: IntegrationType.Analytics, + tags: ['cloud', 'data-analytics'], + bgColor: 'linear-gradient(45deg, #4D27A8 0%, #A166FF 100%)', + icon: AthenaIcon, + subBlocks: [ + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + options: [ + { label: 'Start Query', id: 'start_query' }, + { label: 'Get Query Execution', id: 'get_query_execution' }, + { label: 'Get Query Results', id: 'get_query_results' }, + { label: 'Stop Query', id: 'stop_query' }, + { label: 'List Query Executions', id: 'list_query_executions' }, + { label: 'Create Named Query', id: 'create_named_query' }, + { label: 'Get Named Query', id: 'get_named_query' }, + { label: 'List Named Queries', id: 'list_named_queries' }, + ], + value: () => 'start_query', + }, + { + 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: 'queryString', + title: 'SQL Query', + type: 'code', + placeholder: 'SELECT * FROM my_table LIMIT 10', + condition: { field: 'operation', value: ['start_query', 'create_named_query'] }, + required: { field: 'operation', value: ['start_query', 'create_named_query'] }, + wandConfig: { + enabled: true, + prompt: `Generate an SQL query for AWS Athena based on the user's description. +Athena uses Trino/Presto SQL syntax. Common patterns: +- SELECT * FROM "database"."table" LIMIT 10 +- SELECT column1, COUNT(*) FROM table GROUP BY column1 +- SELECT * FROM table WHERE date_column > DATE '2024-01-01' +- CREATE TABLE new_table AS SELECT ... FROM source_table +- SELECT * FROM table WHERE column IN ('value1', 'value2') + +Return ONLY the SQL query — no explanations, no markdown code blocks.`, + placeholder: 'Describe what data you want to query...', + }, + }, + { + id: 'database', + title: 'Database', + type: 'short-input', + placeholder: 'my_database', + condition: { field: 'operation', value: ['start_query', 'create_named_query'] }, + required: { field: 'operation', value: 'create_named_query' }, + }, + { + id: 'catalog', + title: 'Data Catalog', + type: 'short-input', + placeholder: 'AwsDataCatalog', + condition: { field: 'operation', value: 'start_query' }, + mode: 'advanced', + }, + { + id: 'outputLocation', + title: 'Output Location (S3)', + type: 'short-input', + placeholder: 's3://my-bucket/athena-results/', + condition: { field: 'operation', value: 'start_query' }, + mode: 'advanced', + }, + { + id: 'workGroup', + title: 'Workgroup', + type: 'short-input', + placeholder: 'primary', + condition: { + field: 'operation', + value: ['start_query', 'list_query_executions', 'create_named_query', 'list_named_queries'], + }, + mode: 'advanced', + }, + { + id: 'queryExecutionId', + title: 'Query Execution ID', + type: 'short-input', + placeholder: 'e.g., a1b2c3d4-5678-90ab-cdef-example11111', + condition: { + field: 'operation', + value: ['get_query_execution', 'get_query_results', 'stop_query'], + }, + required: { + field: 'operation', + value: ['get_query_execution', 'get_query_results', 'stop_query'], + }, + }, + { + id: 'namedQueryId', + title: 'Named Query ID', + type: 'short-input', + placeholder: 'e.g., a1b2c3d4-5678-90ab-cdef-example11111', + condition: { field: 'operation', value: 'get_named_query' }, + required: { field: 'operation', value: 'get_named_query' }, + }, + { + id: 'queryName', + title: 'Query Name', + type: 'short-input', + placeholder: 'My Saved Query', + condition: { field: 'operation', value: 'create_named_query' }, + required: { field: 'operation', value: 'create_named_query' }, + }, + { + id: 'queryDescription', + title: 'Description', + type: 'short-input', + placeholder: 'Description of what this query does', + condition: { field: 'operation', value: 'create_named_query' }, + mode: 'advanced', + }, + { + id: 'maxResults', + title: 'Max Results', + type: 'short-input', + placeholder: '50', + condition: { + field: 'operation', + value: ['get_query_results', 'list_query_executions', 'list_named_queries'], + }, + mode: 'advanced', + }, + { + id: 'nextToken', + title: 'Pagination Token', + type: 'short-input', + placeholder: 'Token from previous request', + condition: { + field: 'operation', + value: ['get_query_results', 'list_query_executions', 'list_named_queries'], + }, + mode: 'advanced', + }, + ], + tools: { + access: [ + 'athena_start_query', + 'athena_get_query_execution', + 'athena_get_query_results', + 'athena_stop_query', + 'athena_list_query_executions', + 'athena_create_named_query', + 'athena_get_named_query', + 'athena_list_named_queries', + ], + config: { + tool: (params) => { + switch (params.operation) { + case 'start_query': + return 'athena_start_query' + case 'get_query_execution': + return 'athena_get_query_execution' + case 'get_query_results': + return 'athena_get_query_results' + case 'stop_query': + return 'athena_stop_query' + case 'list_query_executions': + return 'athena_list_query_executions' + case 'create_named_query': + return 'athena_create_named_query' + case 'get_named_query': + return 'athena_get_named_query' + case 'list_named_queries': + return 'athena_list_named_queries' + default: + throw new Error(`Invalid Athena operation: ${params.operation}`) + } + }, + params: (params) => { + const { operation, maxResults, ...rest } = params + + const awsRegion = rest.awsRegion + const awsAccessKeyId = rest.awsAccessKeyId + const awsSecretAccessKey = rest.awsSecretAccessKey + const parsedMaxResults = maxResults ? Number.parseInt(String(maxResults), 10) : undefined + + switch (operation) { + case 'start_query': + return { + awsRegion, + awsAccessKeyId, + awsSecretAccessKey, + queryString: rest.queryString, + ...(rest.database && { database: rest.database }), + ...(rest.catalog && { catalog: rest.catalog }), + ...(rest.outputLocation && { outputLocation: rest.outputLocation }), + ...(rest.workGroup && { workGroup: rest.workGroup }), + } + + case 'get_query_execution': + if (!rest.queryExecutionId) { + throw new Error('Query execution ID is required') + } + return { + awsRegion, + awsAccessKeyId, + awsSecretAccessKey, + queryExecutionId: rest.queryExecutionId, + } + + case 'get_query_results': + if (!rest.queryExecutionId) { + throw new Error('Query execution ID is required') + } + return { + awsRegion, + awsAccessKeyId, + awsSecretAccessKey, + queryExecutionId: rest.queryExecutionId, + ...(parsedMaxResults !== undefined && { maxResults: parsedMaxResults }), + ...(rest.nextToken && { nextToken: rest.nextToken }), + } + + case 'stop_query': + if (!rest.queryExecutionId) { + throw new Error('Query execution ID is required') + } + return { + awsRegion, + awsAccessKeyId, + awsSecretAccessKey, + queryExecutionId: rest.queryExecutionId, + } + + case 'list_query_executions': + return { + awsRegion, + awsAccessKeyId, + awsSecretAccessKey, + ...(rest.workGroup && { workGroup: rest.workGroup }), + ...(parsedMaxResults !== undefined && { maxResults: parsedMaxResults }), + ...(rest.nextToken && { nextToken: rest.nextToken }), + } + + case 'create_named_query': { + if (!rest.queryName) { + throw new Error('Query name is required') + } + if (!rest.database) { + throw new Error('Database is required') + } + if (!rest.queryString) { + throw new Error('SQL query string is required') + } + return { + awsRegion, + awsAccessKeyId, + awsSecretAccessKey, + name: rest.queryName, + database: rest.database, + queryString: rest.queryString, + ...(rest.queryDescription && { description: rest.queryDescription }), + ...(rest.workGroup && { workGroup: rest.workGroup }), + } + } + + case 'get_named_query': + if (!rest.namedQueryId) { + throw new Error('Named query ID is required') + } + return { + awsRegion, + awsAccessKeyId, + awsSecretAccessKey, + namedQueryId: rest.namedQueryId, + } + + case 'list_named_queries': + return { + awsRegion, + awsAccessKeyId, + awsSecretAccessKey, + ...(rest.workGroup && { workGroup: rest.workGroup }), + ...(parsedMaxResults !== undefined && { maxResults: parsedMaxResults }), + ...(rest.nextToken && { nextToken: rest.nextToken }), + } + + default: + throw new Error(`Invalid Athena operation: ${operation}`) + } + }, + }, + }, + inputs: { + operation: { type: 'string', description: 'Athena 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' }, + queryString: { type: 'string', description: 'SQL query string' }, + database: { type: 'string', description: 'Database name' }, + catalog: { type: 'string', description: 'Data catalog name' }, + outputLocation: { type: 'string', description: 'S3 output location for results' }, + workGroup: { type: 'string', description: 'Athena workgroup name' }, + queryExecutionId: { type: 'string', description: 'Query execution ID' }, + namedQueryId: { type: 'string', description: 'Named query ID' }, + queryName: { type: 'string', description: 'Name for a saved query' }, + queryDescription: { type: 'string', description: 'Description for a saved query' }, + maxResults: { type: 'number', description: 'Maximum number of results' }, + nextToken: { type: 'string', description: 'Pagination token' }, + }, + outputs: { + queryExecutionId: { + type: 'string', + description: 'Query execution ID', + }, + query: { + type: 'string', + description: 'SQL query string', + }, + state: { + type: 'string', + description: 'Query state (QUEUED, RUNNING, SUCCEEDED, FAILED, CANCELLED)', + }, + stateChangeReason: { + type: 'string', + description: 'Reason for state change', + }, + statementType: { + type: 'string', + description: 'Statement type (DDL, DML, UTILITY)', + }, + database: { + type: 'string', + description: 'Database name', + }, + catalog: { + type: 'string', + description: 'Data catalog name', + }, + workGroup: { + type: 'string', + description: 'Workgroup name', + }, + submissionDateTime: { + type: 'number', + description: 'Query submission time (Unix epoch ms)', + }, + completionDateTime: { + type: 'number', + description: 'Query completion time (Unix epoch ms)', + }, + dataScannedInBytes: { + type: 'number', + description: 'Data scanned in bytes', + }, + engineExecutionTimeInMillis: { + type: 'number', + description: 'Engine execution time in ms', + }, + queryPlanningTimeInMillis: { + type: 'number', + description: 'Query planning time in ms', + }, + queryQueueTimeInMillis: { + type: 'number', + description: 'Time spent in queue in ms', + }, + totalExecutionTimeInMillis: { + type: 'number', + description: 'Total execution time in ms', + }, + outputLocation: { + type: 'string', + description: 'S3 location of query results', + }, + columns: { + type: 'array', + description: 'Column metadata (name and type)', + }, + rows: { + type: 'array', + description: 'Result rows as key-value objects', + }, + nextToken: { + type: 'string', + description: 'Pagination token for next page', + }, + updateCount: { + type: 'number', + description: 'Rows affected by INSERT/UPDATE', + }, + success: { + type: 'boolean', + description: 'Whether the operation succeeded', + }, + queryExecutionIds: { + type: 'array', + description: 'List of query execution IDs', + }, + namedQueryId: { + type: 'string', + description: 'Named query ID', + }, + name: { + type: 'string', + description: 'Named query name', + }, + description: { + type: 'string', + description: 'Named query description', + }, + queryString: { + type: 'string', + description: 'Named query SQL string', + }, + namedQueryIds: { + type: 'array', + description: 'List of named query IDs', + }, + }, +} diff --git a/apps/sim/blocks/blocks/slack.ts b/apps/sim/blocks/blocks/slack.ts index 70ece5a0e88..84d903c1754 100644 --- a/apps/sim/blocks/blocks/slack.ts +++ b/apps/sim/blocks/blocks/slack.ts @@ -1634,8 +1634,21 @@ Do not include any explanations, markdown formatting, or other text outside the // Trigger outputs (when used as webhook trigger) event_type: { type: 'string', description: 'Type of Slack event that triggered the workflow' }, + subtype: { + type: 'string', + description: + 'Message subtype (e.g., channel_join, channel_leave, bot_message). Null for regular user messages', + }, channel_name: { type: 'string', description: 'Human-readable channel name' }, + channel_type: { + type: 'string', + description: 'Type of channel (e.g., channel, group, im, mpim)', + }, user_name: { type: 'string', description: 'Username who triggered the event' }, + bot_id: { + type: 'string', + description: 'Bot ID if the message was sent by a bot. Null for human users', + }, timestamp: { type: 'string', description: 'Message timestamp from the triggering event' }, thread_ts: { type: 'string', diff --git a/apps/sim/blocks/registry.ts b/apps/sim/blocks/registry.ts index d37f90edfcf..aa915441d73 100644 --- a/apps/sim/blocks/registry.ts +++ b/apps/sim/blocks/registry.ts @@ -13,6 +13,7 @@ import { ApolloBlock } from '@/blocks/blocks/apollo' import { ArxivBlock } from '@/blocks/blocks/arxiv' import { AsanaBlock } from '@/blocks/blocks/asana' import { AshbyBlock } from '@/blocks/blocks/ashby' +import { AthenaBlock } from '@/blocks/blocks/athena' import { AttioBlock } from '@/blocks/blocks/attio' import { BoxBlock } from '@/blocks/blocks/box' import { BrandfetchBlock } from '@/blocks/blocks/brandfetch' @@ -236,6 +237,7 @@ export const registry: Record = { arxiv: ArxivBlock, asana: AsanaBlock, ashby: AshbyBlock, + athena: AthenaBlock, attio: AttioBlock, brandfetch: BrandfetchBlock, box: BoxBlock, diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx index 8b0ed3aabb1..2806cd4d314 100644 --- a/apps/sim/components/icons.tsx +++ b/apps/sim/components/icons.tsx @@ -4687,6 +4687,33 @@ export function CloudFormationIcon(props: SVGProps) { ) } +export function AthenaIcon(props: SVGProps) { + return ( + + + + + + ) +} + export function CloudWatchIcon(props: SVGProps) { return ( | null>(null) + + const handleCopy = useCallback(async () => { + try { + await navigator.clipboard.writeText(code) + setCopied(true) + if (timerRef.current) clearTimeout(timerRef.current) + timerRef.current = setTimeout(() => setCopied(false), 2000) + } catch { + // Clipboard write can fail when document lacks focus or permission is denied + } + }, [code]) + + useEffect( + () => () => { + if (timerRef.current) clearTimeout(timerRef.current) + }, + [] + ) + + return ( + + ) +} diff --git a/apps/sim/connectors/asana/asana.ts b/apps/sim/connectors/asana/asana.ts index e6febeb8210..25009ec5c4a 100644 --- a/apps/sim/connectors/asana/asana.ts +++ b/apps/sim/connectors/asana/asana.ts @@ -2,7 +2,7 @@ import { createLogger } from '@sim/logger' import { AsanaIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' -import { computeContentHash, joinTagArray, parseTagDate } from '@/connectors/utils' +import { joinTagArray, parseTagDate } from '@/connectors/utils' const logger = createLogger('AsanaConnector') @@ -240,7 +240,6 @@ export const asanaConnector: ConnectorConfig = { for (const task of result.data) { const content = buildTaskContent(task) - const contentHash = await computeContentHash(content) const tagNames = task.tags?.map((t) => t.name).filter(Boolean) || [] documents.push({ @@ -249,7 +248,7 @@ export const asanaConnector: ConnectorConfig = { content, mimeType: 'text/plain', sourceUrl: task.permalink_url || undefined, - contentHash, + contentHash: `asana:${task.gid}:${task.modified_at ?? ''}`, metadata: { project: currentProjectGid, assignee: task.assignee?.name, @@ -315,7 +314,6 @@ export const asanaConnector: ConnectorConfig = { if (!task) return null const content = buildTaskContent(task) - const contentHash = await computeContentHash(content) const tagNames = task.tags?.map((t) => t.name).filter(Boolean) || [] return { @@ -324,7 +322,7 @@ export const asanaConnector: ConnectorConfig = { content, mimeType: 'text/plain', sourceUrl: task.permalink_url || undefined, - contentHash, + contentHash: `asana:${task.gid}:${task.modified_at ?? ''}`, metadata: { assignee: task.assignee?.name, completed: task.completed, diff --git a/apps/sim/connectors/fireflies/fireflies.ts b/apps/sim/connectors/fireflies/fireflies.ts index cfb9d0f24a2..c09cccb426d 100644 --- a/apps/sim/connectors/fireflies/fireflies.ts +++ b/apps/sim/connectors/fireflies/fireflies.ts @@ -2,7 +2,7 @@ import { createLogger } from '@sim/logger' import { FirefliesIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' -import { computeContentHash, parseTagDate } from '@/connectors/utils' +import { parseTagDate } from '@/connectors/utils' const logger = createLogger('FirefliesConnector') @@ -196,17 +196,6 @@ export const firefliesConnector: ConnectorConfig = { id name } - sentences { - index - speaker_name - text - } - summary { - keywords - action_items - overview - short_summary - } } }`, variables @@ -214,32 +203,27 @@ export const firefliesConnector: ConnectorConfig = { const transcripts = (data.transcripts || []) as FirefliesTranscript[] - const documents: ExternalDocument[] = await Promise.all( - transcripts.map(async (transcript) => { - const content = formatTranscriptContent(transcript) - const contentHash = await computeContentHash(content) - - const meetingDate = transcript.date ? new Date(transcript.date).toISOString() : undefined - const speakerNames = transcript.speakers?.map((s) => s.name).filter(Boolean) ?? [] - - return { - externalId: transcript.id, - title: transcript.title || 'Untitled Meeting', - content, - mimeType: 'text/plain' as const, - sourceUrl: transcript.transcript_url || undefined, - contentHash, - metadata: { - hostEmail: transcript.host_email, - duration: transcript.duration, - meetingDate, - participants: transcript.participants, - speakers: speakerNames, - keywords: transcript.summary?.keywords, - }, - } - }) - ) + const documents: ExternalDocument[] = transcripts.map((transcript) => { + const meetingDate = transcript.date ? new Date(transcript.date).toISOString() : undefined + const speakerNames = transcript.speakers?.map((s) => s.name).filter(Boolean) ?? [] + + return { + externalId: transcript.id, + title: transcript.title || 'Untitled Meeting', + content: '', + contentDeferred: true, + mimeType: 'text/plain' as const, + sourceUrl: transcript.transcript_url || undefined, + contentHash: `fireflies:${transcript.id}:${transcript.date ?? ''}:${transcript.duration ?? ''}`, + metadata: { + hostEmail: transcript.host_email, + duration: transcript.duration, + meetingDate, + participants: transcript.participants, + speakers: speakerNames, + }, + } + }) const totalFetched = ((syncContext?.totalDocsFetched as number) ?? 0) + documents.length if (syncContext) syncContext.totalDocsFetched = totalFetched @@ -296,7 +280,7 @@ export const firefliesConnector: ConnectorConfig = { if (!transcript) return null const content = formatTranscriptContent(transcript) - const contentHash = await computeContentHash(content) + const contentHash = `fireflies:${transcript.id}:${transcript.date ?? ''}:${transcript.duration ?? ''}` const meetingDate = transcript.date ? new Date(transcript.date).toISOString() : undefined const speakerNames = transcript.speakers?.map((s) => s.name).filter(Boolean) ?? [] @@ -305,6 +289,7 @@ export const firefliesConnector: ConnectorConfig = { externalId: transcript.id, title: transcript.title || 'Untitled Meeting', content, + contentDeferred: false, mimeType: 'text/plain', sourceUrl: transcript.transcript_url || undefined, contentHash, diff --git a/apps/sim/connectors/google-calendar/google-calendar.ts b/apps/sim/connectors/google-calendar/google-calendar.ts index 1fbda8d1a3b..d107cb0569a 100644 --- a/apps/sim/connectors/google-calendar/google-calendar.ts +++ b/apps/sim/connectors/google-calendar/google-calendar.ts @@ -2,7 +2,7 @@ import { createLogger } from '@sim/logger' import { GoogleCalendarIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' -import { computeContentHash, parseTagDate } from '@/connectors/utils' +import { parseTagDate } from '@/connectors/utils' const logger = createLogger('GoogleCalendarConnector') @@ -195,14 +195,12 @@ function getTimeRange(sourceConfig: Record): { timeMin: string; /** * Converts a CalendarEvent to an ExternalDocument. */ -async function eventToDocument(event: CalendarEvent): Promise { +function eventToDocument(event: CalendarEvent): ExternalDocument | null { if (event.status === 'cancelled') return null const content = eventToContent(event) if (!content.trim()) return null - const contentHash = await computeContentHash(content) - const startTime = event.start?.dateTime || event.start?.date || '' const attendeeCount = event.attendees?.filter((a) => !a.resource).length || 0 @@ -212,7 +210,7 @@ async function eventToDocument(event: CalendarEvent): Promise { - try { - const content = await fetchDocContent(accessToken, file.id) - if (!content.trim()) { - logger.info(`Skipping empty document: ${file.name} (${file.id})`) - return null - } - - const contentHash = await computeContentHash(content) - - return { - externalId: file.id, - title: file.name || 'Untitled', - content, - mimeType: 'text/plain', - sourceUrl: file.webViewLink || `https://docs.google.com/document/d/${file.id}/edit`, - contentHash, - metadata: { - modifiedTime: file.modifiedTime, - createdTime: file.createdTime, - owners: file.owners?.map((o) => o.displayName || o.emailAddress).filter(Boolean), - }, - } - } catch (error) { - logger.warn(`Failed to extract content from document: ${file.name} (${file.id})`, { - error: error instanceof Error ? error.message : String(error), - }) - return null +function fileToStub(file: DriveFile): ExternalDocument { + return { + externalId: file.id, + title: file.name || 'Untitled', + content: '', + contentDeferred: true, + mimeType: 'text/plain', + sourceUrl: file.webViewLink || `https://docs.google.com/document/d/${file.id}/edit`, + contentHash: `gdocs:${file.id}:${file.modifiedTime ?? ''}`, + metadata: { + modifiedTime: file.modifiedTime, + createdTime: file.createdTime, + owners: file.owners?.map((o) => o.displayName || o.emailAddress).filter(Boolean), + }, } } @@ -246,18 +229,11 @@ export const googleDocsConnector: ConnectorConfig = { const maxDocs = sourceConfig.maxDocs ? Number(sourceConfig.maxDocs) : 0 const previouslyFetched = (syncContext?.totalDocsFetched as number) ?? 0 - const CONCURRENCY = 5 - const documents: ExternalDocument[] = [] - for (let i = 0; i < files.length; i += CONCURRENCY) { - if (maxDocs > 0 && previouslyFetched + documents.length >= maxDocs) break - const batch = files.slice(i, i + CONCURRENCY) - const results = await Promise.all(batch.map((file) => fileToDocument(accessToken, file))) - documents.push(...(results.filter(Boolean) as ExternalDocument[])) - } + let documents = files.map(fileToStub) if (maxDocs > 0) { const remaining = maxDocs - previouslyFetched if (documents.length > remaining) { - documents.splice(remaining) + documents = documents.slice(0, remaining) } } @@ -300,7 +276,17 @@ export const googleDocsConnector: ConnectorConfig = { if (file.trashed) return null if (file.mimeType !== 'application/vnd.google-apps.document') return null - return fileToDocument(accessToken, file) + try { + const content = await fetchDocContent(accessToken, file.id) + if (!content.trim()) return null + + return { ...fileToStub(file), content, contentDeferred: false } + } catch (error) { + logger.warn(`Failed to extract content from document: ${file.name} (${file.id})`, { + error: error instanceof Error ? error.message : String(error), + }) + return null + } }, validateConfig: async ( diff --git a/apps/sim/connectors/google-sheets/google-sheets.ts b/apps/sim/connectors/google-sheets/google-sheets.ts index 9b70ea9a941..2fb1c1f2865 100644 --- a/apps/sim/connectors/google-sheets/google-sheets.ts +++ b/apps/sim/connectors/google-sheets/google-sheets.ts @@ -2,7 +2,7 @@ import { createLogger } from '@sim/logger' import { GoogleSheetsIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' -import { computeContentHash, parseTagDate } from '@/connectors/utils' +import { parseTagDate } from '@/connectors/utils' const logger = createLogger('GoogleSheetsConnector') @@ -168,7 +168,6 @@ async function sheetToDocument( return null } - const contentHash = await computeContentHash(content) const rowCount = dataRows.length return { @@ -177,7 +176,7 @@ async function sheetToDocument( content, mimeType: 'text/plain', sourceUrl: `https://docs.google.com/spreadsheets/d/${spreadsheetId}/edit#gid=${sheet.sheetId}`, - contentHash, + contentHash: `gsheets:${spreadsheetId}:${sheet.sheetId}:${modifiedTime ?? ''}`, metadata: { spreadsheetId, spreadsheetTitle, @@ -259,22 +258,24 @@ export const googleSheetsConnector: ConnectorConfig = { sheetCount: sheets.length, }) - const documents: ExternalDocument[] = [] - for (let i = 0; i < sheets.length; i += CONCURRENCY) { - const batch = sheets.slice(i, i + CONCURRENCY) - const results = await Promise.all( - batch.map((sheet) => - sheetToDocument( - accessToken, - spreadsheetId, - metadata.properties.title, - sheet, - modifiedTime - ) - ) - ) - documents.push(...(results.filter(Boolean) as ExternalDocument[])) - } + const documents: ExternalDocument[] = sheets.map((sheet) => ({ + externalId: `${spreadsheetId}__sheet__${sheet.sheetId}`, + title: `${metadata.properties.title} - ${sheet.title}`, + content: '', + contentDeferred: true, + mimeType: 'text/plain', + sourceUrl: `https://docs.google.com/spreadsheets/d/${spreadsheetId}/edit#gid=${sheet.sheetId}`, + contentHash: `gsheets:${spreadsheetId}:${sheet.sheetId}:${modifiedTime ?? ''}`, + metadata: { + spreadsheetId, + spreadsheetTitle: metadata.properties.title, + sheetTitle: sheet.title, + sheetId: sheet.sheetId, + rowCount: sheet.gridProperties?.rowCount, + columnCount: sheet.gridProperties?.columnCount, + ...(modifiedTime ? { modifiedTime } : {}), + }, + })) return { documents, @@ -324,13 +325,15 @@ export const googleSheetsConnector: ConnectorConfig = { return null } - return sheetToDocument( + const doc = await sheetToDocument( accessToken, spreadsheetId, metadata.properties.title, sheetEntry.properties, modifiedTime ) + if (!doc) return null + return { ...doc, contentDeferred: false } }, validateConfig: async ( diff --git a/apps/sim/connectors/hubspot/hubspot.ts b/apps/sim/connectors/hubspot/hubspot.ts index 0a8a669284d..2395784e80d 100644 --- a/apps/sim/connectors/hubspot/hubspot.ts +++ b/apps/sim/connectors/hubspot/hubspot.ts @@ -2,7 +2,7 @@ import { createLogger } from '@sim/logger' import { HubspotIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' -import { computeContentHash, parseTagDate } from '@/connectors/utils' +import { parseTagDate } from '@/connectors/utils' const logger = createLogger('HubSpotConnector') @@ -140,16 +140,15 @@ function buildRecordContent(objectType: string, properties: Record, objectType: string, portalId: string -): Promise { +): ExternalDocument { const id = record.id as string const properties = (record.properties || {}) as Record const content = buildRecordContent(objectType, properties) - const contentHash = await computeContentHash(content) const title = buildRecordTitle(objectType, properties) const lastModified = @@ -161,7 +160,7 @@ async function recordToDocument( content, mimeType: 'text/plain', sourceUrl: `https://app.hubspot.com/contacts/${portalId}/record/${objectType}/${id}`, - contentHash, + contentHash: `hubspot:${id}:${lastModified ?? ''}`, metadata: { objectType, owner: properties.hubspot_owner_id || undefined, @@ -260,8 +259,8 @@ export const hubspotConnector: ConnectorConfig = { const paging = data.paging as { next?: { after?: string } } | undefined const nextCursor = paging?.next?.after - const documents: ExternalDocument[] = await Promise.all( - results.map((record) => recordToDocument(record, objectType, portalId)) + const documents: ExternalDocument[] = results.map((record) => + recordToDocument(record, objectType, portalId) ) const previouslyFetched = (syncContext?.totalDocsFetched as number) ?? 0 @@ -322,7 +321,7 @@ export const hubspotConnector: ConnectorConfig = { } const record = await response.json() - return recordToDocument(record, objectType, portalId) + return recordToDocument(record as Record, objectType, portalId) }, validateConfig: async ( diff --git a/apps/sim/connectors/intercom/intercom.ts b/apps/sim/connectors/intercom/intercom.ts index 906607e2487..e7e011d83a3 100644 --- a/apps/sim/connectors/intercom/intercom.ts +++ b/apps/sim/connectors/intercom/intercom.ts @@ -2,7 +2,7 @@ import { createLogger } from '@sim/logger' import { IntercomIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' -import { computeContentHash, htmlToPlainText, parseTagDate } from '@/connectors/utils' +import { htmlToPlainText, parseTagDate } from '@/connectors/utils' const logger = createLogger('IntercomConnector') @@ -309,7 +309,6 @@ export const intercomConnector: ConnectorConfig = { const content = formatArticle(article) if (!content.trim()) continue - const contentHash = await computeContentHash(content) const updatedAt = new Date(article.updated_at * 1000).toISOString() documents.push({ @@ -318,7 +317,7 @@ export const intercomConnector: ConnectorConfig = { content, mimeType: 'text/plain', sourceUrl: `https://app.intercom.com/a/apps/_/articles/articles/${article.id}/show`, - contentHash, + contentHash: `intercom:article-${article.id}:${article.updated_at}`, metadata: { type: 'article', state: article.state, @@ -337,28 +336,23 @@ export const intercomConnector: ConnectorConfig = { const conversations = await fetchConversations(accessToken, maxItems, conversationState) for (const conversation of conversations) { - const detail = await fetchConversationDetail(accessToken, conversation.id) - const content = formatConversation(detail) - if (!content.trim()) continue - - const contentHash = await computeContentHash(content) const updatedAt = new Date(conversation.updated_at * 1000).toISOString() const tags = conversation.tags?.tags?.map((t) => t.name) || [] documents.push({ externalId: `conversation-${conversation.id}`, title: conversation.title || `Conversation #${conversation.id}`, - content, + content: '', + contentDeferred: true, mimeType: 'text/plain', sourceUrl: `https://app.intercom.com/a/apps/_/inbox/inbox/all/conversations/${conversation.id}`, - contentHash, + contentHash: `intercom:conversation-${conversation.id}:${conversation.updated_at}`, metadata: { type: 'conversation', state: conversation.state, tags: tags.join(', '), updatedAt, createdAt: new Date(conversation.created_at * 1000).toISOString(), - messageCount: (detail.conversation_parts?.total_count ?? 0) + 1, }, }) } @@ -383,7 +377,6 @@ export const intercomConnector: ConnectorConfig = { const content = formatArticle(article) if (!content.trim()) return null - const contentHash = await computeContentHash(content) const updatedAt = new Date(article.updated_at * 1000).toISOString() return { @@ -392,7 +385,7 @@ export const intercomConnector: ConnectorConfig = { content, mimeType: 'text/plain', sourceUrl: `https://app.intercom.com/a/apps/_/articles/articles/${article.id}/show`, - contentHash, + contentHash: `intercom:article-${article.id}:${article.updated_at}`, metadata: { type: 'article', state: article.state, @@ -410,7 +403,6 @@ export const intercomConnector: ConnectorConfig = { const content = formatConversation(detail) if (!content.trim()) return null - const contentHash = await computeContentHash(content) const updatedAt = new Date(detail.updated_at * 1000).toISOString() const tags = detail.tags?.tags?.map((t) => t.name) || [] @@ -418,9 +410,10 @@ export const intercomConnector: ConnectorConfig = { externalId, title: detail.title || `Conversation #${detail.id}`, content, + contentDeferred: false, mimeType: 'text/plain', sourceUrl: `https://app.intercom.com/a/apps/_/inbox/inbox/all/conversations/${detail.id}`, - contentHash, + contentHash: `intercom:conversation-${detail.id}:${detail.updated_at}`, metadata: { type: 'conversation', state: detail.state, diff --git a/apps/sim/connectors/jira/jira.ts b/apps/sim/connectors/jira/jira.ts index aaa1218a58f..ea883f77a03 100644 --- a/apps/sim/connectors/jira/jira.ts +++ b/apps/sim/connectors/jira/jira.ts @@ -2,7 +2,7 @@ import { createLogger } from '@sim/logger' import { JiraIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' -import { computeContentHash, joinTagArray, parseTagDate } from '@/connectors/utils' +import { joinTagArray, parseTagDate } from '@/connectors/utils' import { extractAdfText, getJiraCloudId } from '@/tools/jira/utils' const logger = createLogger('JiraConnector') @@ -33,16 +33,12 @@ function buildIssueContent(fields: Record): string { } /** - * Converts a Jira issue API response to an ExternalDocument. + * Extracts common metadata fields from a Jira issue into an ExternalDocument + * stub with deferred content. The contentHash is metadata-based so it is + * identical whether produced during listing or full fetch. */ -async function issueToDocument( - issue: Record, - domain: string -): Promise { +function issueToStub(issue: Record, domain: string): ExternalDocument { const fields = (issue.fields || {}) as Record - const content = buildIssueContent(fields) - const contentHash = await computeContentHash(content) - const key = issue.key as string const issueType = fields.issuetype as Record | undefined const status = fields.status as Record | undefined @@ -51,14 +47,16 @@ async function issueToDocument( const reporter = fields.reporter as Record | undefined const project = fields.project as Record | undefined const labels = Array.isArray(fields.labels) ? (fields.labels as string[]) : [] + const updated = (fields.updated as string) ?? '' return { externalId: String(issue.id), title: `${key}: ${(fields.summary as string) || 'Untitled'}`, - content, + content: '', + contentDeferred: true, mimeType: 'text/plain', sourceUrl: `https://${domain}/browse/${key}`, - contentHash, + contentHash: `jira:${issue.id}:${updated}`, metadata: { key, issueType: issueType?.name, @@ -74,6 +72,22 @@ async function issueToDocument( } } +/** + * Converts a fully-fetched Jira issue (with description and comments) into an + * ExternalDocument with resolved content. + */ +function issueToFullDocument(issue: Record, domain: string): ExternalDocument { + const stub = issueToStub(issue, domain) + const fields = (issue.fields || {}) as Record + const content = buildIssueContent(fields) + + return { + ...stub, + content, + contentDeferred: false, + } +} + export const jiraConnector: ConnectorConfig = { id: 'jira', name: 'Jira', @@ -162,7 +176,7 @@ export const jiraConnector: ConnectorConfig = { params.append('maxResults', String(Math.min(PAGE_SIZE, remaining))) params.append( 'fields', - 'summary,description,comment,issuetype,status,priority,assignee,reporter,project,labels,created,updated' + 'summary,issuetype,status,priority,assignee,reporter,project,labels,created,updated' ) const url = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/search?${params.toString()}` @@ -190,9 +204,7 @@ export const jiraConnector: ConnectorConfig = { const issues = (data.issues || []) as Record[] const total = (data.total as number) ?? 0 - const documents: ExternalDocument[] = await Promise.all( - issues.map((issue) => issueToDocument(issue, domain)) - ) + const documents: ExternalDocument[] = issues.map((issue) => issueToStub(issue, domain)) const nextStart = startAt + issues.length const hasMore = nextStart < total && (maxIssues <= 0 || nextStart < maxIssues) @@ -239,7 +251,7 @@ export const jiraConnector: ConnectorConfig = { } const issue = await response.json() - return issueToDocument(issue, domain) + return issueToFullDocument(issue, domain) }, validateConfig: async ( diff --git a/apps/sim/connectors/linear/linear.ts b/apps/sim/connectors/linear/linear.ts index fbcb2dde60e..efe391c7c00 100644 --- a/apps/sim/connectors/linear/linear.ts +++ b/apps/sim/connectors/linear/linear.ts @@ -3,7 +3,7 @@ import { LinearIcon } from '@/components/icons' import type { RetryOptions } from '@/lib/knowledge/documents/utils' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' -import { computeContentHash, joinTagArray, parseTagDate } from '@/connectors/utils' +import { joinTagArray, parseTagDate } from '@/connectors/utils' const logger = createLogger('LinearConnector') @@ -278,36 +278,34 @@ export const linearConnector: ConnectorConfig = { const nodes = (issuesConn.nodes || []) as Record[] const pageInfo = issuesConn.pageInfo as Record - const documents: ExternalDocument[] = await Promise.all( - nodes.map(async (issue) => { - const content = buildIssueContent(issue) - const contentHash = await computeContentHash(content) + const documents: ExternalDocument[] = nodes.map((issue) => { + const content = buildIssueContent(issue) + const contentHash = `linear:${issue.id}:${issue.updatedAt}` - const labelNodes = ((issue.labels as Record)?.nodes || []) as Record< - string, - unknown - >[] + const labelNodes = ((issue.labels as Record)?.nodes || []) as Record< + string, + unknown + >[] - return { - externalId: issue.id as string, - title: `${(issue.identifier as string) || ''}: ${(issue.title as string) || 'Untitled'}`, - content, - mimeType: 'text/plain' as const, - sourceUrl: (issue.url as string) || undefined, - contentHash, - metadata: { - identifier: issue.identifier, - state: (issue.state as Record)?.name, - priority: issue.priorityLabel, - assignee: (issue.assignee as Record)?.name, - labels: labelNodes.map((l) => l.name as string), - team: (issue.team as Record)?.name, - project: (issue.project as Record)?.name, - lastModified: issue.updatedAt, - }, - } - }) - ) + return { + externalId: issue.id as string, + title: `${(issue.identifier as string) || ''}: ${(issue.title as string) || 'Untitled'}`, + content, + mimeType: 'text/plain' as const, + sourceUrl: (issue.url as string) || undefined, + contentHash, + metadata: { + identifier: issue.identifier, + state: (issue.state as Record)?.name, + priority: issue.priorityLabel, + assignee: (issue.assignee as Record)?.name, + labels: labelNodes.map((l) => l.name as string), + team: (issue.team as Record)?.name, + project: (issue.project as Record)?.name, + lastModified: issue.updatedAt, + }, + } + }) const hasNextPage = Boolean(pageInfo.hasNextPage) const endCursor = (pageInfo.endCursor as string) || undefined @@ -335,7 +333,7 @@ export const linearConnector: ConnectorConfig = { if (!issue) return null const content = buildIssueContent(issue) - const contentHash = await computeContentHash(content) + const contentHash = `linear:${issue.id}:${issue.updatedAt}` const labelNodes = ((issue.labels as Record)?.nodes || []) as Record< string, @@ -346,7 +344,7 @@ export const linearConnector: ConnectorConfig = { externalId: issue.id as string, title: `${(issue.identifier as string) || ''}: ${(issue.title as string) || 'Untitled'}`, content, - mimeType: 'text/plain', + mimeType: 'text/plain' as const, sourceUrl: (issue.url as string) || undefined, contentHash, metadata: { @@ -379,7 +377,6 @@ export const linearConnector: ConnectorConfig = { } try { - // Verify the token works by fetching teams const data = await linearGraphQL(accessToken, TEAMS_QUERY, undefined, VALIDATE_RETRY_OPTIONS) const teamsConn = data.teams as Record const teams = (teamsConn.nodes || []) as Record[] @@ -391,7 +388,6 @@ export const linearConnector: ConnectorConfig = { } } - // If teamId specified, verify it exists const teamId = sourceConfig.teamId as string | undefined if (teamId) { const found = teams.some((t) => t.id === teamId) diff --git a/apps/sim/connectors/outlook/outlook.ts b/apps/sim/connectors/outlook/outlook.ts index 1c800701caa..d36635d6d9b 100644 --- a/apps/sim/connectors/outlook/outlook.ts +++ b/apps/sim/connectors/outlook/outlook.ts @@ -2,14 +2,37 @@ import { createLogger } from '@sim/logger' import { OutlookIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' -import { computeContentHash, htmlToPlainText, parseTagDate } from '@/connectors/utils' +import { htmlToPlainText, parseTagDate } from '@/connectors/utils' const logger = createLogger('OutlookConnector') const GRAPH_API_BASE = 'https://graph.microsoft.com/v1.0/me' const DEFAULT_MAX_CONVERSATIONS = 500 const MESSAGES_PER_PAGE = 50 -const MESSAGE_FIELDS = [ +/** + * Fields requested when listing messages (no body — deferred to getDocument). + */ +const LIST_MESSAGE_FIELDS = [ + 'id', + 'conversationId', + 'subject', + 'from', + 'toRecipients', + 'receivedDateTime', + 'sentDateTime', + 'categories', + 'importance', + 'inferenceClassification', + 'hasAttachments', + 'webLink', + 'isDraft', + 'parentFolderId', +].join(',') + +/** + * Fields requested when fetching full message content in getDocument. + */ +const FULL_MESSAGE_FIELDS = [ 'id', 'conversationId', 'subject', @@ -84,7 +107,7 @@ function buildInitialUrl(sourceConfig: Record): string { const params = new URLSearchParams({ $top: String(MESSAGES_PER_PAGE), - $select: MESSAGE_FIELDS, + $select: LIST_MESSAGE_FIELDS, }) // Build $filter clauses @@ -353,7 +376,6 @@ export const outlookConnector: ConnectorConfig = { const headers: Record = { Authorization: `Bearer ${accessToken}`, Accept: 'application/json', - Prefer: 'outlook.body-content-type="text"', } const response = await fetchWithRetry(url, { method: 'GET', headers }) @@ -385,7 +407,8 @@ export const outlookConnector: ConnectorConfig = { continue } - const convId = msg.conversationId || msg.id + if (!msg.conversationId) continue + const convId = msg.conversationId if (!conversations[convId]) { conversations[convId] = [] } @@ -407,8 +430,8 @@ export const outlookConnector: ConnectorConfig = { } } - // Phase 2: Group conversations into documents - logger.info('Grouping Outlook messages into conversations', { + // Phase 2: Build lightweight stubs — content is deferred to getDocument + logger.info('Building Outlook conversation stubs', { totalMessages: syncContext?._totalMessagesFetched, totalConversations: Object.keys(conversations).length, }) @@ -433,23 +456,26 @@ export const outlookConnector: ConnectorConfig = { const documents: ExternalDocument[] = [] for (const [convId, msgs] of limited) { - const result = formatConversation(convId, msgs) - if (!result) continue + if (msgs.length === 0) continue - const contentHash = await computeContentHash(result.content) + const lastDate = msgs.reduce((max, m) => { + const d = m.receivedDateTime || '' + return d > max ? d : max + }, '') - // Use the first message's webLink as the source URL + const subject = msgs[0].subject || 'No Subject' const firstWithLink = msgs.find((m) => m.webLink) - const sourceUrl = firstWithLink?.webLink || `https://outlook.office.com/mail/inbox` + const sourceUrl = firstWithLink?.webLink || 'https://outlook.office.com/mail/inbox' documents.push({ externalId: convId, - title: result.subject, - content: result.content, + title: subject, + content: '', + contentDeferred: true, mimeType: 'text/plain', sourceUrl, - contentHash, - metadata: result.metadata, + contentHash: `outlook:${convId}:${lastDate}`, + metadata: {}, }) } @@ -462,14 +488,25 @@ export const outlookConnector: ConnectorConfig = { externalId: string ): Promise => { try { - // Fetch messages for this conversation + // Scope to the same folder as listDocuments so contentHash stays consistent + const folder = (sourceConfig.folder as string) || 'inbox' + const basePath = + folder === 'all' + ? `${GRAPH_API_BASE}/messages` + : `${GRAPH_API_BASE}/mailFolders/${WELL_KNOWN_FOLDERS[folder] || folder}/messages` + + const filterParts = [ + `conversationId eq '${externalId.replace(/'/g, "''")}'`, + 'isDraft eq false', + ] + const params = new URLSearchParams({ - $filter: `conversationId eq '${externalId.replace(/'/g, "''")}'`, - $select: MESSAGE_FIELDS, - $top: '50', + $filter: filterParts.join(' and '), + $select: FULL_MESSAGE_FIELDS, + $top: '250', }) - const url = `${GRAPH_API_BASE}/messages?${params.toString()}` + const url = `${basePath}?${params.toString()}` const response = await fetchWithRetry(url, { method: 'GET', @@ -493,16 +530,21 @@ export const outlookConnector: ConnectorConfig = { const result = formatConversation(externalId, messages) if (!result) return null - const contentHash = await computeContentHash(result.content) + const lastDate = messages.reduce((max, m) => { + const d = m.receivedDateTime || '' + return d > max ? d : max + }, '') + const firstWithLink = messages.find((m) => m.webLink) return { externalId, title: result.subject, content: result.content, + contentDeferred: false, mimeType: 'text/plain', sourceUrl: firstWithLink?.webLink || 'https://outlook.office.com/mail/inbox', - contentHash, + contentHash: `outlook:${externalId}:${lastDate}`, metadata: result.metadata, } } catch (error) { diff --git a/apps/sim/connectors/reddit/reddit.ts b/apps/sim/connectors/reddit/reddit.ts index b74a8d4b0b3..ce636ad1be7 100644 --- a/apps/sim/connectors/reddit/reddit.ts +++ b/apps/sim/connectors/reddit/reddit.ts @@ -2,7 +2,7 @@ import { createLogger } from '@sim/logger' import { RedditIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' -import { computeContentHash, parseTagDate } from '@/connectors/utils' +import { parseTagDate } from '@/connectors/utils' const logger = createLogger('RedditConnector') @@ -338,29 +338,23 @@ export const redditConnector: ConnectorConfig = { afterToken ) - const documents: ExternalDocument[] = [] - - for (const post of posts) { - const content = await formatPostContent(accessToken, post, COMMENTS_PER_POST) - const contentHash = await computeContentHash(content) - - documents.push({ - externalId: post.id, - title: post.title, - content, - mimeType: 'text/plain', - sourceUrl: `https://www.reddit.com${post.permalink}`, - contentHash, - metadata: { - author: post.author, - score: post.score, - commentCount: post.num_comments, - flair: post.link_flair_text ?? undefined, - postDate: new Date(post.created_utc * 1000).toISOString(), - subreddit: post.subreddit, - }, - }) - } + const documents: ExternalDocument[] = posts.map((post) => ({ + externalId: post.id, + title: post.title, + content: '', + contentDeferred: true, + mimeType: 'text/plain', + sourceUrl: `https://www.reddit.com${post.permalink}`, + contentHash: `reddit:${post.id}:${post.created_utc}`, + metadata: { + author: post.author, + score: post.score, + commentCount: post.num_comments, + flair: post.link_flair_text ?? undefined, + postDate: new Date(post.created_utc * 1000).toISOString(), + subreddit: post.subreddit, + }, + })) const totalCollected = collectedSoFar + documents.length const hasMore = after !== null && totalCollected < maxPosts @@ -397,15 +391,15 @@ export const redditConnector: ConnectorConfig = { const comments = data.length >= 2 ? extractComments(data[1] as RedditListing, COMMENTS_PER_POST) : [] const content = await formatPostContent(accessToken, post, COMMENTS_PER_POST, comments) - const contentHash = await computeContentHash(content) return { externalId: post.id, title: post.title, content, + contentDeferred: false, mimeType: 'text/plain', sourceUrl: `https://www.reddit.com${post.permalink}`, - contentHash, + contentHash: `reddit:${post.id}:${post.created_utc}`, metadata: { author: post.author, score: post.score, diff --git a/apps/sim/connectors/salesforce/salesforce.ts b/apps/sim/connectors/salesforce/salesforce.ts index c829187c699..c090ad91559 100644 --- a/apps/sim/connectors/salesforce/salesforce.ts +++ b/apps/sim/connectors/salesforce/salesforce.ts @@ -2,7 +2,7 @@ import { createLogger } from '@sim/logger' import { SalesforceIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' -import { computeContentHash, htmlToPlainText, parseTagDate } from '@/connectors/utils' +import { htmlToPlainText, parseTagDate } from '@/connectors/utils' const logger = createLogger('SalesforceConnector') @@ -12,7 +12,14 @@ const PAGE_SIZE = 200 /** SOQL field lists per object type. */ const OBJECT_FIELDS: Record = { - KnowledgeArticleVersion: ['Id', 'Title', 'Summary', 'LastModifiedDate', 'ArticleNumber'], + KnowledgeArticleVersion: [ + 'Id', + 'Title', + 'Summary', + 'LastModifiedDate', + 'ArticleNumber', + 'PublishStatus', + ], Case: ['Id', 'Subject', 'Description', 'Status', 'LastModifiedDate', 'CaseNumber'], Account: ['Id', 'Name', 'Description', 'Industry', 'LastModifiedDate'], Opportunity: [ @@ -146,36 +153,52 @@ function getRecordStatus(objectType: string, record: Record): s } /** - * Converts a Salesforce record to an ExternalDocument. + * Creates a lightweight stub for a Salesforce record with metadata-based hash. + * Content is deferred and fetched later via getDocument only for new/changed docs. */ -async function recordToDocument( +function recordToStub( record: Record, objectType: string, instanceUrl: string -): Promise { +): ExternalDocument { const id = record.Id as string - const content = buildRecordContent(objectType, record) - const contentHash = await computeContentHash(content) const title = buildRecordTitle(objectType, record) - + const lastModified = (record.LastModifiedDate as string) || '' const baseUrl = instanceUrl.replace(`/services/data/${API_VERSION}/`, '') return { externalId: id, title, - content, + content: '', + contentDeferred: true, mimeType: 'text/plain', sourceUrl: `${baseUrl}/${id}`, - contentHash, + contentHash: `salesforce:${id}:${lastModified}`, metadata: { objectType, - lastModified: (record.LastModifiedDate as string) || undefined, + lastModified: lastModified || undefined, recordNumber: getRecordNumber(objectType, record), status: getRecordStatus(objectType, record), }, } } +/** + * Builds a full ExternalDocument with content from a Salesforce record. + */ +function recordToDocument( + record: Record, + objectType: string, + instanceUrl: string +): ExternalDocument { + const stub = recordToStub(record, objectType, instanceUrl) + return { + ...stub, + content: buildRecordContent(objectType, record), + contentDeferred: false, + } +} + export const salesforceConnector: ConnectorConfig = { id: 'salesforce', name: 'Salesforce', @@ -257,8 +280,8 @@ export const salesforceConnector: ConnectorConfig = { const records = (data.records || []) as Record[] const nextRecordsUrl = data.nextRecordsUrl as string | undefined - const documents: ExternalDocument[] = await Promise.all( - records.map((record) => recordToDocument(record, objectType, instanceUrl)) + const documents: ExternalDocument[] = records.map((record) => + recordToStub(record, objectType, instanceUrl) ) const previouslyFetched = (syncContext?.totalDocsFetched as number) ?? 0 diff --git a/apps/sim/connectors/servicenow/servicenow.ts b/apps/sim/connectors/servicenow/servicenow.ts index 3aefb66fbdc..ebd33e79056 100644 --- a/apps/sim/connectors/servicenow/servicenow.ts +++ b/apps/sim/connectors/servicenow/servicenow.ts @@ -2,7 +2,7 @@ import { createLogger } from '@sim/logger' import { ServiceNowIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' -import { computeContentHash, htmlToPlainText, parseTagDate } from '@/connectors/utils' +import { htmlToPlainText, parseTagDate } from '@/connectors/utils' const logger = createLogger('ServiceNowConnector') @@ -184,15 +184,13 @@ function priorityLabel(priority: string | undefined): string { /** * Converts a KB article record to an ExternalDocument. */ -async function kbArticleToDocument( - article: KBArticle, - instanceUrl: string -): Promise { +function kbArticleToDocument(article: KBArticle, instanceUrl: string): ExternalDocument { const title = rawValue(article.short_description) || rawValue(article.number) || article.sys_id const articleText = rawValue(article.text) || rawValue(article.wiki) || '' const content = htmlToPlainText(articleText) - const contentHash = await computeContentHash(content) const sysId = rawValue(article.sys_id as unknown as string) || article.sys_id + const updatedOn = rawValue(article.sys_updated_on) || '' + const contentHash = `servicenow:${sysId}:${updatedOn}` const sourceUrl = `${instanceUrl}/kb_view.do?sys_kb_id=${sysId}` return { @@ -218,10 +216,7 @@ async function kbArticleToDocument( /** * Converts an incident record to an ExternalDocument. */ -async function incidentToDocument( - incident: Incident, - instanceUrl: string -): Promise { +function incidentToDocument(incident: Incident, instanceUrl: string): ExternalDocument { const number = rawValue(incident.number) const shortDesc = rawValue(incident.short_description) const title = number ? `${number}: ${shortDesc || 'Untitled'}` : shortDesc || incident.sys_id @@ -258,8 +253,9 @@ async function incidentToDocument( } const content = parts.join('\n') - const contentHash = await computeContentHash(content) const sysId = rawValue(incident.sys_id as unknown as string) || incident.sys_id + const updatedOn = rawValue(incident.sys_updated_on) || '' + const contentHash = `servicenow:${sysId}:${updatedOn}` const sourceUrl = `${instanceUrl}/incident.do?sys_id=${sysId}` return { @@ -478,8 +474,8 @@ export const servicenowConnector: ConnectorConfig = { const documents: ExternalDocument[] = [] for (const record of result) { const doc = isKB - ? await kbArticleToDocument(record as unknown as KBArticle, instanceUrl) - : await incidentToDocument(record as unknown as Incident, instanceUrl) + ? kbArticleToDocument(record as unknown as KBArticle, instanceUrl) + : incidentToDocument(record as unknown as Incident, instanceUrl) if (doc.content.trim()) { documents.push(doc) @@ -532,8 +528,8 @@ export const servicenowConnector: ConnectorConfig = { const record = result[0] const doc = isKB - ? await kbArticleToDocument(record as unknown as KBArticle, instanceUrl) - : await incidentToDocument(record as unknown as Incident, instanceUrl) + ? kbArticleToDocument(record as unknown as KBArticle, instanceUrl) + : incidentToDocument(record as unknown as Incident, instanceUrl) return doc.content.trim() ? doc : null } catch (error) { diff --git a/apps/sim/connectors/webflow/webflow.ts b/apps/sim/connectors/webflow/webflow.ts index c398ed815f1..b0ee6932fac 100644 --- a/apps/sim/connectors/webflow/webflow.ts +++ b/apps/sim/connectors/webflow/webflow.ts @@ -2,7 +2,7 @@ import { createLogger } from '@sim/logger' import { WebflowIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' -import { computeContentHash, htmlToPlainText, parseTagDate } from '@/connectors/utils' +import { htmlToPlainText, parseTagDate } from '@/connectors/utils' const logger = createLogger('WebflowConnector') @@ -194,8 +194,8 @@ export const webflowConnector: ConnectorConfig = { } const items = data.items || [] - let documents: ExternalDocument[] = await Promise.all( - items.map((item) => itemToDocument(item, currentCollectionId, collectionName)) + let documents: ExternalDocument[] = items.map((item) => + itemToDocument(item, currentCollectionId, collectionName) ) if (maxItems > 0) { @@ -373,13 +373,14 @@ export const webflowConnector: ConnectorConfig = { /** * Converts a Webflow CMS item to an ExternalDocument. */ -async function itemToDocument( +function itemToDocument( item: WebflowItem, collectionId: string, collectionName: string -): Promise { +): ExternalDocument { const plainText = itemToPlainText(item, collectionName) - const contentHash = await computeContentHash(plainText) + const lastModified = item.lastUpdated || item.lastPublished || item.createdOn || '' + const contentHash = `webflow:${item.id}:${lastModified}` const title = extractItemTitle(item) const slug = (item.fieldData?.slug as string) || '' diff --git a/apps/sim/connectors/wordpress/wordpress.ts b/apps/sim/connectors/wordpress/wordpress.ts index 9f835e52e06..eb079f104a9 100644 --- a/apps/sim/connectors/wordpress/wordpress.ts +++ b/apps/sim/connectors/wordpress/wordpress.ts @@ -2,7 +2,7 @@ import { createLogger } from '@sim/logger' import { WordpressIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' -import { computeContentHash, htmlToPlainText, joinTagArray, parseTagDate } from '@/connectors/utils' +import { htmlToPlainText, joinTagArray, parseTagDate } from '@/connectors/utils' const logger = createLogger('WordPressConnector') @@ -59,10 +59,10 @@ function extractTagNames(tags: Record): string[] { /** * Converts a WordPress post to an ExternalDocument. */ -async function postToDocument(post: WordPressPost): Promise { +function postToDocument(post: WordPressPost): ExternalDocument { const plainText = htmlToPlainText(post.content) const fullContent = `# ${post.title}\n\n${plainText}` - const contentHash = await computeContentHash(fullContent) + const contentHash = `wordpress:${post.ID}:${post.modified || ''}` const categories = extractCategoryNames(post.categories) const tags = extractTagNames(post.tags) @@ -182,7 +182,7 @@ export const wordpressConnector: ConnectorConfig = { const data = (await response.json()) as WordPressPostsResponse const posts = data.posts || [] - const documents = await Promise.all(posts.map(postToDocument)) + const documents = posts.map(postToDocument) const totalFetched = totalDocsFetched + documents.length if (syncContext) syncContext.totalDocsFetched = totalFetched @@ -226,7 +226,7 @@ export const wordpressConnector: ConnectorConfig = { } const post = (await response.json()) as WordPressPost - return await postToDocument(post) + return postToDocument(post) } catch (error) { logger.warn('Failed to get WordPress document', { externalId, diff --git a/apps/sim/connectors/zendesk/zendesk.ts b/apps/sim/connectors/zendesk/zendesk.ts index d42d016366a..6bdf57e6108 100644 --- a/apps/sim/connectors/zendesk/zendesk.ts +++ b/apps/sim/connectors/zendesk/zendesk.ts @@ -2,7 +2,7 @@ import { createLogger } from '@sim/logger' import { ZendeskIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' -import { computeContentHash, htmlToPlainText, joinTagArray, parseTagDate } from '@/connectors/utils' +import { htmlToPlainText, joinTagArray, parseTagDate } from '@/connectors/utils' const logger = createLogger('ZendeskConnector') @@ -207,14 +207,11 @@ function formatTicketContent(ticket: ZendeskTicket, comments: ZendeskComment[]): } /** - * Converts an article to an ExternalDocument. + * Converts an article to an ExternalDocument with inline content. + * Articles return body inline from the list API so no deferral is needed. */ -async function articleToDocument( - article: ZendeskArticle, - subdomain: string -): Promise { +function articleToDocument(article: ZendeskArticle, subdomain: string): ExternalDocument { const content = htmlToPlainText(article.body || '') - const contentHash = await computeContentHash(content) return { externalId: `article-${article.id}`, @@ -222,7 +219,7 @@ async function articleToDocument( content, mimeType: 'text/plain', sourceUrl: article.html_url || `https://${subdomain}.zendesk.com/hc/articles/${article.id}`, - contentHash, + contentHash: `zendesk:article:${article.id}:${article.updated_at}`, metadata: { type: 'article', articleId: article.id, @@ -238,23 +235,50 @@ async function articleToDocument( } /** - * Converts a ticket (with comments) to an ExternalDocument. + * Creates a deferred stub for a ticket. Content is not fetched here because + * each ticket requires a separate comments API call. Full content is fetched + * lazily via getDocument only for new/changed documents. + */ +function ticketToStub(ticket: ZendeskTicket, subdomain: string): ExternalDocument { + return { + externalId: `ticket-${ticket.id}`, + title: `Ticket #${ticket.id}: ${ticket.subject}`, + content: '', + contentDeferred: true, + mimeType: 'text/plain', + sourceUrl: `https://${subdomain}.zendesk.com/agent/tickets/${ticket.id}`, + contentHash: `zendesk:ticket:${ticket.id}:${ticket.updated_at}`, + metadata: { + type: 'ticket', + ticketId: ticket.id, + status: ticket.status, + priority: ticket.priority, + tags: ticket.tags, + createdAt: ticket.created_at, + updatedAt: ticket.updated_at, + }, + } +} + +/** + * Converts a ticket (with comments) to a full ExternalDocument. + * Used by getDocument to resolve deferred ticket stubs. */ -async function ticketToDocument( +function ticketToDocument( ticket: ZendeskTicket, comments: ZendeskComment[], subdomain: string -): Promise { +): ExternalDocument { const content = formatTicketContent(ticket, comments) - const contentHash = await computeContentHash(content) return { externalId: `ticket-${ticket.id}`, title: `Ticket #${ticket.id}: ${ticket.subject}`, content, + contentDeferred: false, mimeType: 'text/plain', sourceUrl: `https://${subdomain}.zendesk.com/agent/tickets/${ticket.id}`, - contentHash, + contentHash: `zendesk:ticket:${ticket.id}:${ticket.updated_at}`, metadata: { type: 'ticket', ticketId: ticket.id, @@ -375,8 +399,7 @@ export const zendeskConnector: ConnectorConfig = { for (const article of articles) { if (!article.body?.trim()) continue - const doc = await articleToDocument(article, subdomain) - documents.push(doc) + documents.push(articleToDocument(article, subdomain)) } } @@ -391,21 +414,8 @@ export const zendeskConnector: ConnectorConfig = { ) logger.info(`Fetched ${tickets.length} tickets from Zendesk`) - const BATCH_SIZE = 5 - for (let i = 0; i < tickets.length; i += BATCH_SIZE) { - const batch = tickets.slice(i, i + BATCH_SIZE) - const batchResults = await Promise.all( - batch.map(async (ticket) => { - const comments = await fetchTicketComments( - subdomain, - accessToken, - sourceConfig, - ticket.id - ) - return ticketToDocument(ticket, comments, subdomain) - }) - ) - documents.push(...batchResults) + for (const ticket of tickets) { + documents.push(ticketToStub(ticket, subdomain)) } } diff --git a/apps/sim/hooks/queries/environment.ts b/apps/sim/hooks/queries/environment.ts index ebc5f2430bc..575b09b0661 100644 --- a/apps/sim/hooks/queries/environment.ts +++ b/apps/sim/hooks/queries/environment.ts @@ -1,8 +1,7 @@ import { createLogger } from '@sim/logger' import { keepPreviousData, useMutation, useQuery, useQueryClient } from '@tanstack/react-query' -import type { EnvironmentVariable, WorkspaceEnvironmentData } from '@/lib/environment/api' +import type { WorkspaceEnvironmentData } from '@/lib/environment/api' import { fetchPersonalEnvironment, fetchWorkspaceEnvironment } from '@/lib/environment/api' -import { workspaceCredentialKeys } from '@/hooks/queries/credentials' import { API_ENDPOINTS } from '@/stores/constants' const logger = createLogger('EnvironmentQueries') @@ -56,28 +55,10 @@ export function useSavePersonalEnvironment() { return useMutation({ mutationFn: async ({ variables }: SavePersonalEnvironmentParams) => { - const transformedVariables = Object.entries(variables).reduce( - (acc, [key, value]) => ({ - ...acc, - [key]: { key, value }, - }), - {} - ) - const response = await fetch(API_ENDPOINTS.ENVIRONMENT, { method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - variables: Object.entries(transformedVariables).reduce( - (acc, [key, value]) => ({ - ...acc, - [key]: (value as EnvironmentVariable).value, - }), - {} - ), - }), + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ variables }), }) if (!response.ok) { @@ -85,11 +66,9 @@ export function useSavePersonalEnvironment() { } logger.info('Saved personal environment variables') - return transformedVariables }, onSettled: () => { queryClient.invalidateQueries({ queryKey: environmentKeys.personal() }) - queryClient.invalidateQueries({ queryKey: workspaceCredentialKeys.lists() }) }, }) } @@ -124,8 +103,6 @@ export function useUpsertWorkspaceEnvironment() { queryClient.invalidateQueries({ queryKey: environmentKeys.workspace(variables.workspaceId), }) - queryClient.invalidateQueries({ queryKey: environmentKeys.personal() }) - queryClient.invalidateQueries({ queryKey: workspaceCredentialKeys.lists() }) }, }) } @@ -160,8 +137,6 @@ export function useRemoveWorkspaceEnvironment() { queryClient.invalidateQueries({ queryKey: environmentKeys.workspace(variables.workspaceId), }) - queryClient.invalidateQueries({ queryKey: environmentKeys.personal() }) - queryClient.invalidateQueries({ queryKey: workspaceCredentialKeys.lists() }) }, }) } diff --git a/apps/sim/hooks/queries/tasks.ts b/apps/sim/hooks/queries/tasks.ts index 2c38f339558..9cd1eab999a 100644 --- a/apps/sim/hooks/queries/tasks.ts +++ b/apps/sim/hooks/queries/tasks.ts @@ -1,5 +1,5 @@ import { keepPreviousData, useMutation, useQuery, useQueryClient } from '@tanstack/react-query' -import type { MothershipResource } from '@/app/workspace/[workspaceId]/home/types' +import type { ChatContextKind, MothershipResource } from '@/app/workspace/[workspaceId]/home/types' export interface TaskMetadata { id: string @@ -42,13 +42,14 @@ export interface TaskStoredFileAttachment { } export interface TaskStoredMessageContext { - kind: string + kind: ChatContextKind label: string workflowId?: string knowledgeId?: string tableId?: string fileId?: string folderId?: string + chatId?: string } export interface TaskStoredMessage { diff --git a/apps/sim/lib/auth/auth.ts b/apps/sim/lib/auth/auth.ts index 6c51ab2d16b..98e48bd7803 100644 --- a/apps/sim/lib/auth/auth.ts +++ b/apps/sim/lib/auth/auth.ts @@ -82,6 +82,7 @@ import { quickValidateEmail } from '@/lib/messaging/email/validation' import { scheduleLifecycleEmail } from '@/lib/messaging/lifecycle' import { captureServerEvent } from '@/lib/posthog/server' import { syncAllWebhooksForCredentialSet } from '@/lib/webhooks/utils.server' +import { disableUserResources } from '@/lib/workflows/lifecycle' import { SSO_TRUSTED_PROVIDERS } from '@/ee/sso/constants' import { createAnonymousSession, ensureAnonymousUserExists } from './anonymous' @@ -243,6 +244,13 @@ export const auth = betterAuth({ } }, }, + update: { + after: async (user) => { + if (user.banned) { + await disableUserResources(user.id) + } + }, + }, }, account: { create: { diff --git a/apps/sim/lib/copilot/resource-types.ts b/apps/sim/lib/copilot/resource-types.ts index c0e83fe8a46..a223536925c 100644 --- a/apps/sim/lib/copilot/resource-types.ts +++ b/apps/sim/lib/copilot/resource-types.ts @@ -4,6 +4,7 @@ export type MothershipResourceType = | 'workflow' | 'knowledgebase' | 'folder' + | 'task' | 'generic' export interface MothershipResource { @@ -19,3 +20,9 @@ export const VFS_DIR_TO_RESOURCE: Record = { knowledgebases: 'knowledgebase', folders: 'folder', } as const + +/** MIME type for a single dragged resource (used by resource-tabs internal reordering). */ +export const SIM_RESOURCE_DRAG_TYPE = 'application/x-sim-resource' as const + +/** MIME type for an array of dragged resources (used by sidebar drag-to-chat). */ +export const SIM_RESOURCES_DRAG_TYPE = 'application/x-sim-resources' as const diff --git a/apps/sim/lib/core/utils/react-node-text.ts b/apps/sim/lib/core/utils/react-node-text.ts new file mode 100644 index 00000000000..c59e865a9b2 --- /dev/null +++ b/apps/sim/lib/core/utils/react-node-text.ts @@ -0,0 +1,14 @@ +import { isValidElement, type ReactNode } from 'react' + +/** + * Recursively extracts plain text content from a React node tree. + */ +export function extractTextContent(node: ReactNode): string { + if (typeof node === 'string') return node + if (typeof node === 'number') return String(node) + if (!node) return '' + if (Array.isArray(node)) return node.map(extractTextContent).join('') + if (isValidElement(node)) + return extractTextContent((node.props as { children?: ReactNode }).children) + return '' +} diff --git a/apps/sim/lib/posthog/events.ts b/apps/sim/lib/posthog/events.ts index 537a9864282..faf9895bf62 100644 --- a/apps/sim/lib/posthog/events.ts +++ b/apps/sim/lib/posthog/events.ts @@ -378,6 +378,11 @@ export interface PostHogEventMap { workspace_id: string } + task_generation_aborted: { + workspace_id: string + view: 'mothership' | 'copilot' + } + task_message_sent: { workspace_id: string has_attachments: boolean diff --git a/apps/sim/lib/webhooks/gmail-polling-service.ts b/apps/sim/lib/webhooks/gmail-polling-service.ts deleted file mode 100644 index 6c916cc3cf8..00000000000 --- a/apps/sim/lib/webhooks/gmail-polling-service.ts +++ /dev/null @@ -1,791 +0,0 @@ -import { db } from '@sim/db' -import { - account, - credentialSet, - webhook, - workflow, - workflowDeploymentVersion, -} from '@sim/db/schema' -import { createLogger } from '@sim/logger' -import { and, eq, isNull, or, sql } from 'drizzle-orm' -import { isOrganizationOnTeamOrEnterprisePlan } from '@/lib/billing' -import { pollingIdempotency } from '@/lib/core/idempotency/service' -import { getInternalApiBaseUrl } from '@/lib/core/utils/urls' -import { generateShortId } from '@/lib/core/utils/uuid' -import { - getOAuthToken, - refreshAccessTokenIfNeeded, - resolveOAuthAccountId, -} from '@/app/api/auth/oauth/utils' -import type { GmailAttachment } from '@/tools/gmail/types' -import { downloadAttachments, extractAttachmentInfo } from '@/tools/gmail/utils' -import { MAX_CONSECUTIVE_FAILURES } from '@/triggers/constants' - -const logger = createLogger('GmailPollingService') - -interface GmailWebhookConfig { - labelIds: string[] - labelFilterBehavior: 'INCLUDE' | 'EXCLUDE' - markAsRead: boolean - searchQuery?: string - maxEmailsPerPoll?: number - lastCheckedTimestamp?: string - historyId?: string - includeAttachments?: boolean - includeRawEmail?: boolean -} - -interface GmailEmail { - id: string - threadId: string - historyId?: string - labelIds?: string[] - payload?: any - snippet?: string - internalDate?: string -} - -export interface SimplifiedEmail { - id: string - threadId: string - subject: string - from: string - to: string - cc: string - date: string | null - bodyText: string - bodyHtml: string - labels: string[] - hasAttachments: boolean - attachments: GmailAttachment[] -} - -export interface GmailWebhookPayload { - email: SimplifiedEmail - timestamp: string - rawEmail?: GmailEmail // Only included when includeRawEmail is true -} - -async function markWebhookFailed(webhookId: string) { - try { - const result = await db - .update(webhook) - .set({ - failedCount: sql`COALESCE(${webhook.failedCount}, 0) + 1`, - lastFailedAt: new Date(), - updatedAt: new Date(), - }) - .where(eq(webhook.id, webhookId)) - .returning({ failedCount: webhook.failedCount }) - - const newFailedCount = result[0]?.failedCount || 0 - const shouldDisable = newFailedCount >= MAX_CONSECUTIVE_FAILURES - - if (shouldDisable) { - await db - .update(webhook) - .set({ - isActive: false, - updatedAt: new Date(), - }) - .where(eq(webhook.id, webhookId)) - - logger.warn( - `Webhook ${webhookId} auto-disabled after ${MAX_CONSECUTIVE_FAILURES} consecutive failures` - ) - } - } catch (err) { - logger.error(`Failed to mark webhook ${webhookId} as failed:`, err) - } -} - -async function markWebhookSuccess(webhookId: string) { - try { - await db - .update(webhook) - .set({ - failedCount: 0, // Reset on success - updatedAt: new Date(), - }) - .where(eq(webhook.id, webhookId)) - } catch (err) { - logger.error(`Failed to mark webhook ${webhookId} as successful:`, err) - } -} - -export async function pollGmailWebhooks() { - logger.info('Starting Gmail webhook polling') - - try { - const activeWebhooksResult = await db - .select({ webhook }) - .from(webhook) - .innerJoin(workflow, eq(webhook.workflowId, workflow.id)) - .leftJoin( - workflowDeploymentVersion, - and( - eq(workflowDeploymentVersion.workflowId, workflow.id), - eq(workflowDeploymentVersion.isActive, true) - ) - ) - .where( - and( - eq(webhook.provider, 'gmail'), - eq(webhook.isActive, true), - eq(workflow.isDeployed, true), - or( - eq(webhook.deploymentVersionId, workflowDeploymentVersion.id), - and(isNull(workflowDeploymentVersion.id), isNull(webhook.deploymentVersionId)) - ) - ) - ) - - const activeWebhooks = activeWebhooksResult.map((r) => r.webhook) - - if (!activeWebhooks.length) { - logger.info('No active Gmail webhooks found') - return { total: 0, successful: 0, failed: 0, details: [] } - } - - logger.info(`Found ${activeWebhooks.length} active Gmail webhooks`) - - // Limit the number of webhooks processed in parallel to avoid - // exhausting Postgres or Gmail API connections when many users exist. - const CONCURRENCY = 10 - - const running: Promise[] = [] - let successCount = 0 - let failureCount = 0 - - const enqueue = async (webhookData: (typeof activeWebhooks)[number]) => { - const webhookId = webhookData.id - const requestId = generateShortId() - - try { - const metadata = webhookData.providerConfig as any - const credentialId: string | undefined = metadata?.credentialId - const userId: string | undefined = metadata?.userId - const credentialSetId: string | undefined = webhookData.credentialSetId ?? undefined - - if (!credentialId && !userId) { - logger.error(`[${requestId}] Missing credential info for webhook ${webhookId}`) - await markWebhookFailed(webhookId) - failureCount++ - return - } - - if (credentialSetId) { - const [cs] = await db - .select({ organizationId: credentialSet.organizationId }) - .from(credentialSet) - .where(eq(credentialSet.id, credentialSetId)) - .limit(1) - - if (cs?.organizationId) { - const hasAccess = await isOrganizationOnTeamOrEnterprisePlan(cs.organizationId) - if (!hasAccess) { - logger.error( - `[${requestId}] Polling Group plan restriction: Your current plan does not support Polling Groups. Upgrade to Team or Enterprise to use this feature.`, - { - webhookId, - credentialSetId, - organizationId: cs.organizationId, - } - ) - await markWebhookFailed(webhookId) - failureCount++ - return - } - } - } - - let accessToken: string | null = null - - if (credentialId) { - const resolved = await resolveOAuthAccountId(credentialId) - if (!resolved) { - logger.error( - `[${requestId}] Failed to resolve OAuth account for credential ${credentialId}, webhook ${webhookId}` - ) - await markWebhookFailed(webhookId) - failureCount++ - return - } - const rows = await db - .select() - .from(account) - .where(eq(account.id, resolved.accountId)) - .limit(1) - if (rows.length === 0) { - logger.error( - `[${requestId}] Credential ${credentialId} not found for webhook ${webhookId}` - ) - await markWebhookFailed(webhookId) - failureCount++ - return - } - const ownerUserId = rows[0].userId - accessToken = await refreshAccessTokenIfNeeded(resolved.accountId, ownerUserId, requestId) - } else if (userId) { - // Legacy fallback for webhooks without credentialId - accessToken = await getOAuthToken(userId, 'google-email') - } - - if (!accessToken) { - logger.error(`[${requestId}] Failed to get Gmail access token for webhook ${webhookId}`) - await markWebhookFailed(webhookId) - failureCount++ - return - } - - const config = webhookData.providerConfig as unknown as GmailWebhookConfig - - const now = new Date() - - const fetchResult = await fetchNewEmails(accessToken, config, requestId) - - const { emails, latestHistoryId } = fetchResult - - if (!emails || !emails.length) { - await updateWebhookLastChecked( - webhookId, - now.toISOString(), - latestHistoryId || config.historyId - ) - await markWebhookSuccess(webhookId) - logger.info(`[${requestId}] No new emails found for webhook ${webhookId}`) - successCount++ - return - } - - logger.info(`[${requestId}] Found ${emails.length} new emails for webhook ${webhookId}`) - - logger.info(`[${requestId}] Processing ${emails.length} emails for webhook ${webhookId}`) - - const emailsToProcess = emails - - const { processedCount, failedCount } = await processEmails( - emailsToProcess, - webhookData, - config, - accessToken, - requestId - ) - - await updateWebhookLastChecked( - webhookId, - now.toISOString(), - latestHistoryId || config.historyId - ) - - if (failedCount > 0 && processedCount === 0) { - await markWebhookFailed(webhookId) - failureCount++ - logger.warn( - `[${requestId}] All ${failedCount} emails failed to process for webhook ${webhookId}` - ) - } else { - await markWebhookSuccess(webhookId) - successCount++ - logger.info( - `[${requestId}] Successfully processed ${processedCount} emails for webhook ${webhookId}${failedCount > 0 ? ` (${failedCount} failed)` : ''}` - ) - } - } catch (error) { - logger.error(`[${requestId}] Error processing Gmail webhook ${webhookId}:`, error) - await markWebhookFailed(webhookId) - failureCount++ - } - } - - for (const webhookData of activeWebhooks) { - const promise: Promise = enqueue(webhookData) - .catch((err) => { - logger.error('Unexpected error in webhook processing:', err) - failureCount++ - }) - .finally(() => { - const idx = running.indexOf(promise) - if (idx !== -1) running.splice(idx, 1) - }) - - running.push(promise) - - if (running.length >= CONCURRENCY) { - await Promise.race(running) - } - } - - await Promise.allSettled(running) - - const summary = { - total: activeWebhooks.length, - successful: successCount, - failed: failureCount, - details: [], - } - - logger.info('Gmail polling completed', { - total: summary.total, - successful: summary.successful, - failed: summary.failed, - }) - - return summary - } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error' - logger.error('Error in Gmail polling service:', errorMessage) - throw error - } -} - -async function fetchNewEmails(accessToken: string, config: GmailWebhookConfig, requestId: string) { - try { - const useHistoryApi = !!config.historyId - let emails = [] - let latestHistoryId = config.historyId - - if (useHistoryApi) { - const historyUrl = `https://gmail.googleapis.com/gmail/v1/users/me/history?startHistoryId=${config.historyId}` - - const historyResponse = await fetch(historyUrl, { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }) - - if (!historyResponse.ok) { - const errorData = await historyResponse.json() - logger.error(`[${requestId}] Gmail history API error:`, { - status: historyResponse.status, - statusText: historyResponse.statusText, - error: errorData, - }) - - logger.info(`[${requestId}] Falling back to search API after history API failure`) - const searchResult = await searchEmails(accessToken, config, requestId) - return { - emails: searchResult.emails, - latestHistoryId: searchResult.latestHistoryId, - } - } - - const historyData = await historyResponse.json() - - if (!historyData.history || !historyData.history.length) { - return { emails: [], latestHistoryId } - } - - if (historyData.historyId) { - latestHistoryId = historyData.historyId - } - - const messageIds = new Set() - - for (const history of historyData.history) { - if (history.messagesAdded) { - for (const messageAdded of history.messagesAdded) { - messageIds.add(messageAdded.message.id) - } - } - } - - if (messageIds.size === 0) { - return { emails: [], latestHistoryId } - } - - const sortedIds = [...messageIds].sort().reverse() - - const idsToFetch = sortedIds.slice(0, config.maxEmailsPerPoll || 25) - logger.info(`[${requestId}] Processing ${idsToFetch.length} emails from history API`) - - const emailPromises = idsToFetch.map(async (messageId) => { - return getEmailDetails(accessToken, messageId) - }) - - const emailResults = await Promise.allSettled(emailPromises) - const rejected = emailResults.filter((r) => r.status === 'rejected') - if (rejected.length > 0) { - logger.warn(`[${requestId}] Failed to fetch ${rejected.length} email details`) - } - emails = emailResults - .filter( - (result): result is PromiseFulfilledResult => result.status === 'fulfilled' - ) - .map((result) => result.value) - - emails = filterEmailsByLabels(emails, config) - } else { - const searchResult = await searchEmails(accessToken, config, requestId) - return searchResult - } - - return { emails, latestHistoryId } - } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error' - logger.error(`[${requestId}] Error fetching new emails:`, errorMessage) - throw error - } -} - -/** - * Builds a Gmail search query from label and search configuration - */ -function buildGmailSearchQuery(config: { - labelIds?: string[] - labelFilterBehavior?: 'INCLUDE' | 'EXCLUDE' - searchQuery?: string -}): string { - let labelQuery = '' - if (config.labelIds && config.labelIds.length > 0) { - const labelParts = config.labelIds.map((label) => `label:${label}`).join(' OR ') - labelQuery = - config.labelFilterBehavior === 'INCLUDE' - ? config.labelIds.length > 1 - ? `(${labelParts})` - : labelParts - : config.labelIds.length > 1 - ? `-(${labelParts})` - : `-${labelParts}` - } - - let searchQueryPart = '' - if (config.searchQuery?.trim()) { - searchQueryPart = config.searchQuery.trim() - if (searchQueryPart.includes(' OR ') || searchQueryPart.includes(' AND ')) { - searchQueryPart = `(${searchQueryPart})` - } - } - - let baseQuery = '' - if (labelQuery && searchQueryPart) { - baseQuery = `${labelQuery} ${searchQueryPart}` - } else if (searchQueryPart) { - baseQuery = searchQueryPart - } else if (labelQuery) { - baseQuery = labelQuery - } else { - baseQuery = 'in:inbox' - } - - return baseQuery -} - -async function searchEmails(accessToken: string, config: GmailWebhookConfig, requestId: string) { - try { - const baseQuery = buildGmailSearchQuery(config) - - let timeConstraint = '' - - if (config.lastCheckedTimestamp) { - const lastCheckedTime = new Date(config.lastCheckedTimestamp) - const now = new Date() - const minutesSinceLastCheck = (now.getTime() - lastCheckedTime.getTime()) / (60 * 1000) - - if (minutesSinceLastCheck < 60) { - const bufferSeconds = Math.max(1 * 60 * 2, 180) - - const cutoffTime = new Date(lastCheckedTime.getTime() - bufferSeconds * 1000) - - const timestamp = Math.floor(cutoffTime.getTime() / 1000) - - timeConstraint = ` after:${timestamp}` - } else if (minutesSinceLastCheck < 24 * 60) { - const hours = Math.ceil(minutesSinceLastCheck / 60) + 1 // Round up and add 1 hour buffer - timeConstraint = ` newer_than:${hours}h` - } else { - const days = Math.min(Math.ceil(minutesSinceLastCheck / (24 * 60)), 7) + 1 - timeConstraint = ` newer_than:${days}d` - } - } else { - timeConstraint = ' newer_than:1d' - } - - const query = `${baseQuery}${timeConstraint}` - - const searchUrl = `https://gmail.googleapis.com/gmail/v1/users/me/messages?q=${encodeURIComponent(query)}&maxResults=${config.maxEmailsPerPoll || 25}` - - const searchResponse = await fetch(searchUrl, { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }) - - if (!searchResponse.ok) { - const errorData = await searchResponse.json() - logger.error(`[${requestId}] Gmail search API error:`, { - status: searchResponse.status, - statusText: searchResponse.statusText, - query: query, - error: errorData, - }) - throw new Error( - `Gmail API error: ${searchResponse.status} ${searchResponse.statusText} - ${JSON.stringify(errorData)}` - ) - } - - const searchData = await searchResponse.json() - - if (!searchData.messages || !searchData.messages.length) { - logger.info(`[${requestId}] No emails found matching query: ${query}`) - return { emails: [], latestHistoryId: config.historyId } - } - - const idsToFetch = searchData.messages.slice(0, config.maxEmailsPerPoll || 25) - let latestHistoryId = config.historyId - - logger.info( - `[${requestId}] Processing ${idsToFetch.length} emails from search API (total matches: ${searchData.messages.length})` - ) - - const emailPromises = idsToFetch.map(async (message: { id: string }) => { - return getEmailDetails(accessToken, message.id) - }) - - const emailResults = await Promise.allSettled(emailPromises) - const rejected = emailResults.filter((r) => r.status === 'rejected') - if (rejected.length > 0) { - logger.warn(`[${requestId}] Failed to fetch ${rejected.length} email details`) - } - const emails = emailResults - .filter( - (result): result is PromiseFulfilledResult => result.status === 'fulfilled' - ) - .map((result) => result.value) - - if (emails.length > 0 && emails[0].historyId) { - latestHistoryId = emails[0].historyId - } - - return { emails, latestHistoryId } - } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error' - logger.error(`[${requestId}] Error searching emails:`, errorMessage) - throw error - } -} - -async function getEmailDetails(accessToken: string, messageId: string): Promise { - const messageUrl = `https://gmail.googleapis.com/gmail/v1/users/me/messages/${messageId}?format=full` - - const messageResponse = await fetch(messageUrl, { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }) - - if (!messageResponse.ok) { - const errorData = await messageResponse.json().catch(() => ({})) - throw new Error( - `Failed to fetch email details for message ${messageId}: ${messageResponse.status} ${messageResponse.statusText} - ${JSON.stringify(errorData)}` - ) - } - - return await messageResponse.json() -} - -function filterEmailsByLabels(emails: GmailEmail[], config: GmailWebhookConfig): GmailEmail[] { - if (!config.labelIds.length) { - return emails - } - - return emails.filter((email) => { - const emailLabels = email.labelIds || [] - const hasMatchingLabel = config.labelIds.some((configLabel) => - emailLabels.includes(configLabel) - ) - - return config.labelFilterBehavior === 'INCLUDE' - ? hasMatchingLabel // Include emails with matching labels - : !hasMatchingLabel // Exclude emails with matching labels - }) -} - -async function processEmails( - emails: any[], - webhookData: any, - config: GmailWebhookConfig, - accessToken: string, - requestId: string -) { - let processedCount = 0 - let failedCount = 0 - - for (const email of emails) { - try { - await pollingIdempotency.executeWithIdempotency( - 'gmail', - `${webhookData.id}:${email.id}`, - async () => { - const headers: Record = {} - if (email.payload?.headers) { - for (const header of email.payload.headers) { - headers[header.name.toLowerCase()] = header.value - } - } - - let textContent = '' - let htmlContent = '' - - const extractContent = (part: any) => { - if (!part) return - - if (part.mimeType === 'text/plain' && part.body?.data) { - textContent = Buffer.from(part.body.data, 'base64').toString('utf-8') - } else if (part.mimeType === 'text/html' && part.body?.data) { - htmlContent = Buffer.from(part.body.data, 'base64').toString('utf-8') - } - - if (part.parts && Array.isArray(part.parts)) { - for (const subPart of part.parts) { - extractContent(subPart) - } - } - } - - if (email.payload) { - extractContent(email.payload) - } - - let date: string | null = null - if (headers.date) { - try { - date = new Date(headers.date).toISOString() - } catch (_e) { - // Keep date as null if parsing fails - } - } else if (email.internalDate) { - date = new Date(Number.parseInt(email.internalDate)).toISOString() - } - - let attachments: GmailAttachment[] = [] - const hasAttachments = email.payload - ? extractAttachmentInfo(email.payload).length > 0 - : false - - if (config.includeAttachments && hasAttachments && email.payload) { - try { - const attachmentInfo = extractAttachmentInfo(email.payload) - attachments = await downloadAttachments(email.id, attachmentInfo, accessToken) - } catch (error) { - logger.error( - `[${requestId}] Error downloading attachments for email ${email.id}:`, - error - ) - } - } - - const simplifiedEmail: SimplifiedEmail = { - id: email.id, - threadId: email.threadId, - subject: headers.subject || '[No Subject]', - from: headers.from || '', - to: headers.to || '', - cc: headers.cc || '', - date: date, - bodyText: textContent, - bodyHtml: htmlContent, - labels: email.labelIds || [], - hasAttachments, - attachments, - } - - const payload: GmailWebhookPayload = { - email: simplifiedEmail, - timestamp: new Date().toISOString(), - ...(config.includeRawEmail ? { rawEmail: email } : {}), - } - - const webhookUrl = `${getInternalApiBaseUrl()}/api/webhooks/trigger/${webhookData.path}` - - const response = await fetch(webhookUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'User-Agent': 'Sim/1.0', - }, - body: JSON.stringify(payload), - }) - - if (!response.ok) { - const errorText = await response.text() - logger.error( - `[${requestId}] Failed to trigger webhook for email ${email.id}:`, - response.status, - errorText - ) - throw new Error(`Webhook request failed: ${response.status} - ${errorText}`) - } - - if (config.markAsRead) { - await markEmailAsRead(accessToken, email.id) - } - - return { - emailId: email.id, - webhookStatus: response.status, - processed: true, - } - } - ) - - logger.info( - `[${requestId}] Successfully processed email ${email.id} for webhook ${webhookData.id}` - ) - processedCount++ - } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error' - logger.error(`[${requestId}] Error processing email ${email.id}:`, errorMessage) - failedCount++ - } - } - - return { processedCount, failedCount } -} - -async function markEmailAsRead(accessToken: string, messageId: string) { - const modifyUrl = `https://gmail.googleapis.com/gmail/v1/users/me/messages/${messageId}/modify` - - try { - const response = await fetch(modifyUrl, { - method: 'POST', - headers: { - Authorization: `Bearer ${accessToken}`, - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - removeLabelIds: ['UNREAD'], - }), - }) - - if (!response.ok) { - await response.body?.cancel().catch(() => {}) - throw new Error( - `Failed to mark email ${messageId} as read: ${response.status} ${response.statusText}` - ) - } - } catch (error) { - logger.error(`Error marking email ${messageId} as read:`, error) - throw error - } -} - -async function updateWebhookLastChecked(webhookId: string, timestamp: string, historyId?: string) { - try { - const result = await db.select().from(webhook).where(eq(webhook.id, webhookId)) - const existingConfig = (result[0]?.providerConfig as Record) || {} - await db - .update(webhook) - .set({ - providerConfig: { - ...existingConfig, - lastCheckedTimestamp: timestamp, - ...(historyId ? { historyId } : {}), - } as any, - updatedAt: new Date(), - }) - .where(eq(webhook.id, webhookId)) - } catch (error) { - logger.error(`Error updating webhook ${webhookId} last checked timestamp:`, error) - } -} diff --git a/apps/sim/lib/webhooks/polling/gmail.ts b/apps/sim/lib/webhooks/polling/gmail.ts new file mode 100644 index 00000000000..7db8587d2c2 --- /dev/null +++ b/apps/sim/lib/webhooks/polling/gmail.ts @@ -0,0 +1,553 @@ +import { pollingIdempotency } from '@/lib/core/idempotency/service' +import type { PollingProviderHandler, PollWebhookContext } from '@/lib/webhooks/polling/types' +import { + markWebhookFailed, + markWebhookSuccess, + resolveOAuthCredential, + updateWebhookProviderConfig, +} from '@/lib/webhooks/polling/utils' +import { processPolledWebhookEvent } from '@/lib/webhooks/processor' +import type { GmailAttachment } from '@/tools/gmail/types' +import { downloadAttachments, extractAttachmentInfo } from '@/tools/gmail/utils' + +interface GmailWebhookConfig { + labelIds: string[] + labelFilterBehavior: 'INCLUDE' | 'EXCLUDE' + markAsRead: boolean + searchQuery?: string + maxEmailsPerPoll?: number + lastCheckedTimestamp?: string + historyId?: string + includeAttachments?: boolean + includeRawEmail?: boolean +} + +interface GmailEmail { + id: string + threadId: string + historyId?: string + labelIds?: string[] + payload?: Record + snippet?: string + internalDate?: string +} + +export interface SimplifiedEmail { + id: string + threadId: string + subject: string + from: string + to: string + cc: string + date: string | null + bodyText: string + bodyHtml: string + labels: string[] + hasAttachments: boolean + attachments: GmailAttachment[] +} + +export interface GmailWebhookPayload { + email: SimplifiedEmail + timestamp: string + rawEmail?: GmailEmail +} + +export const gmailPollingHandler: PollingProviderHandler = { + provider: 'gmail', + label: 'Gmail', + + async pollWebhook(ctx: PollWebhookContext): Promise<'success' | 'failure'> { + const { webhookData, workflowData, requestId, logger } = ctx + const webhookId = webhookData.id + + try { + const accessToken = await resolveOAuthCredential( + webhookData, + 'google-email', + requestId, + logger + ) + + const config = webhookData.providerConfig as unknown as GmailWebhookConfig + const now = new Date() + + const { emails, latestHistoryId } = await fetchNewEmails( + accessToken, + config, + requestId, + logger + ) + + if (!emails || !emails.length) { + await updateWebhookProviderConfig( + webhookId, + { + lastCheckedTimestamp: now.toISOString(), + ...(latestHistoryId || config.historyId + ? { historyId: latestHistoryId || config.historyId } + : {}), + }, + logger + ) + await markWebhookSuccess(webhookId, logger) + logger.info(`[${requestId}] No new emails found for webhook ${webhookId}`) + return 'success' + } + + logger.info(`[${requestId}] Found ${emails.length} new emails for webhook ${webhookId}`) + + const { processedCount, failedCount } = await processEmails( + emails, + webhookData, + workflowData, + config, + accessToken, + requestId, + logger + ) + + await updateWebhookProviderConfig( + webhookId, + { + lastCheckedTimestamp: now.toISOString(), + ...(latestHistoryId || config.historyId + ? { historyId: latestHistoryId || config.historyId } + : {}), + }, + logger + ) + + if (failedCount > 0 && processedCount === 0) { + await markWebhookFailed(webhookId, logger) + logger.warn( + `[${requestId}] All ${failedCount} emails failed to process for webhook ${webhookId}` + ) + return 'failure' + } + + await markWebhookSuccess(webhookId, logger) + logger.info( + `[${requestId}] Successfully processed ${processedCount} emails for webhook ${webhookId}${failedCount > 0 ? ` (${failedCount} failed)` : ''}` + ) + return 'success' + } catch (error) { + logger.error(`[${requestId}] Error processing Gmail webhook ${webhookId}:`, error) + await markWebhookFailed(webhookId, logger) + return 'failure' + } + }, +} + +async function fetchNewEmails( + accessToken: string, + config: GmailWebhookConfig, + requestId: string, + logger: ReturnType +) { + try { + const useHistoryApi = !!config.historyId + let emails: GmailEmail[] = [] + let latestHistoryId = config.historyId + + if (useHistoryApi) { + const historyUrl = `https://gmail.googleapis.com/gmail/v1/users/me/history?startHistoryId=${config.historyId}` + + const historyResponse = await fetch(historyUrl, { + headers: { Authorization: `Bearer ${accessToken}` }, + }) + + if (!historyResponse.ok) { + const errorData = await historyResponse.json() + logger.error(`[${requestId}] Gmail history API error:`, { + status: historyResponse.status, + statusText: historyResponse.statusText, + error: errorData, + }) + + logger.info(`[${requestId}] Falling back to search API after history API failure`) + return searchEmails(accessToken, config, requestId, logger) + } + + const historyData = await historyResponse.json() + + if (!historyData.history || !historyData.history.length) { + return { emails: [], latestHistoryId } + } + + if (historyData.historyId) { + latestHistoryId = historyData.historyId + } + + const messageIds = new Set() + for (const history of historyData.history) { + if (history.messagesAdded) { + for (const messageAdded of history.messagesAdded) { + messageIds.add(messageAdded.message.id) + } + } + } + + if (messageIds.size === 0) { + return { emails: [], latestHistoryId } + } + + const sortedIds = [...messageIds].sort().reverse() + const idsToFetch = sortedIds.slice(0, config.maxEmailsPerPoll || 25) + logger.info(`[${requestId}] Processing ${idsToFetch.length} emails from history API`) + + const emailResults = await Promise.allSettled( + idsToFetch.map((messageId) => getEmailDetails(accessToken, messageId)) + ) + const rejected = emailResults.filter((r) => r.status === 'rejected') + if (rejected.length > 0) { + logger.warn(`[${requestId}] Failed to fetch ${rejected.length} email details`) + } + emails = emailResults + .filter( + (result): result is PromiseFulfilledResult => result.status === 'fulfilled' + ) + .map((result) => result.value) + + emails = filterEmailsByLabels(emails, config) + } else { + return searchEmails(accessToken, config, requestId, logger) + } + + return { emails, latestHistoryId } + } catch (error) { + const errorMessage = error instanceof Error ? error.message : 'Unknown error' + logger.error(`[${requestId}] Error fetching new emails:`, errorMessage) + throw error + } +} + +function buildGmailSearchQuery(config: { + labelIds?: string[] + labelFilterBehavior?: 'INCLUDE' | 'EXCLUDE' + searchQuery?: string +}): string { + let labelQuery = '' + if (config.labelIds && config.labelIds.length > 0) { + const labelParts = config.labelIds.map((label) => `label:${label}`).join(' OR ') + labelQuery = + config.labelFilterBehavior === 'INCLUDE' + ? config.labelIds.length > 1 + ? `(${labelParts})` + : labelParts + : config.labelIds.length > 1 + ? `-(${labelParts})` + : `-${labelParts}` + } + + let searchQueryPart = '' + if (config.searchQuery?.trim()) { + searchQueryPart = config.searchQuery.trim() + if (searchQueryPart.includes(' OR ') || searchQueryPart.includes(' AND ')) { + searchQueryPart = `(${searchQueryPart})` + } + } + + let baseQuery = '' + if (labelQuery && searchQueryPart) { + baseQuery = `${labelQuery} ${searchQueryPart}` + } else if (searchQueryPart) { + baseQuery = searchQueryPart + } else if (labelQuery) { + baseQuery = labelQuery + } else { + baseQuery = 'in:inbox' + } + + return baseQuery +} + +async function searchEmails( + accessToken: string, + config: GmailWebhookConfig, + requestId: string, + logger: ReturnType +) { + try { + const baseQuery = buildGmailSearchQuery(config) + let timeConstraint = '' + + if (config.lastCheckedTimestamp) { + const lastCheckedTime = new Date(config.lastCheckedTimestamp) + const now = new Date() + const minutesSinceLastCheck = (now.getTime() - lastCheckedTime.getTime()) / (60 * 1000) + + if (minutesSinceLastCheck < 60) { + const bufferSeconds = Math.max(1 * 60 * 2, 180) + const cutoffTime = new Date(lastCheckedTime.getTime() - bufferSeconds * 1000) + const timestamp = Math.floor(cutoffTime.getTime() / 1000) + timeConstraint = ` after:${timestamp}` + } else if (minutesSinceLastCheck < 24 * 60) { + const hours = Math.ceil(minutesSinceLastCheck / 60) + 1 + timeConstraint = ` newer_than:${hours}h` + } else { + const days = Math.min(Math.ceil(minutesSinceLastCheck / (24 * 60)), 7) + 1 + timeConstraint = ` newer_than:${days}d` + } + } else { + timeConstraint = ' newer_than:1d' + } + + const query = `${baseQuery}${timeConstraint}` + const searchUrl = `https://gmail.googleapis.com/gmail/v1/users/me/messages?q=${encodeURIComponent(query)}&maxResults=${config.maxEmailsPerPoll || 25}` + + const searchResponse = await fetch(searchUrl, { + headers: { Authorization: `Bearer ${accessToken}` }, + }) + + if (!searchResponse.ok) { + const errorData = await searchResponse.json() + logger.error(`[${requestId}] Gmail search API error:`, { + status: searchResponse.status, + statusText: searchResponse.statusText, + query, + error: errorData, + }) + throw new Error( + `Gmail API error: ${searchResponse.status} ${searchResponse.statusText} - ${JSON.stringify(errorData)}` + ) + } + + const searchData = await searchResponse.json() + + if (!searchData.messages || !searchData.messages.length) { + logger.info(`[${requestId}] No emails found matching query: ${query}`) + return { emails: [], latestHistoryId: config.historyId } + } + + const idsToFetch = searchData.messages.slice(0, config.maxEmailsPerPoll || 25) + let latestHistoryId = config.historyId + + logger.info( + `[${requestId}] Processing ${idsToFetch.length} emails from search API (total matches: ${searchData.messages.length})` + ) + + const emailResults = await Promise.allSettled( + idsToFetch.map((message: { id: string }) => getEmailDetails(accessToken, message.id)) + ) + const rejected = emailResults.filter((r) => r.status === 'rejected') + if (rejected.length > 0) { + logger.warn(`[${requestId}] Failed to fetch ${rejected.length} email details`) + } + const emails = emailResults + .filter( + (result): result is PromiseFulfilledResult => result.status === 'fulfilled' + ) + .map((result) => result.value) + + if (emails.length > 0 && emails[0].historyId) { + latestHistoryId = emails[0].historyId + } + + return { emails, latestHistoryId } + } catch (error) { + const errorMessage = error instanceof Error ? error.message : 'Unknown error' + logger.error(`[${requestId}] Error searching emails:`, errorMessage) + throw error + } +} + +async function getEmailDetails(accessToken: string, messageId: string): Promise { + const messageUrl = `https://gmail.googleapis.com/gmail/v1/users/me/messages/${messageId}?format=full` + + const messageResponse = await fetch(messageUrl, { + headers: { Authorization: `Bearer ${accessToken}` }, + }) + + if (!messageResponse.ok) { + const errorData = await messageResponse.json().catch(() => ({})) + throw new Error( + `Failed to fetch email details for message ${messageId}: ${messageResponse.status} ${messageResponse.statusText} - ${JSON.stringify(errorData)}` + ) + } + + return await messageResponse.json() +} + +function filterEmailsByLabels(emails: GmailEmail[], config: GmailWebhookConfig): GmailEmail[] { + if (!config.labelIds.length) { + return emails + } + + return emails.filter((email) => { + const emailLabels = email.labelIds || [] + const hasMatchingLabel = config.labelIds.some((configLabel) => + emailLabels.includes(configLabel) + ) + return config.labelFilterBehavior === 'INCLUDE' ? hasMatchingLabel : !hasMatchingLabel + }) +} + +async function processEmails( + emails: GmailEmail[], + webhookData: PollWebhookContext['webhookData'], + workflowData: PollWebhookContext['workflowData'], + config: GmailWebhookConfig, + accessToken: string, + requestId: string, + logger: ReturnType +) { + let processedCount = 0 + let failedCount = 0 + + for (const email of emails) { + try { + await pollingIdempotency.executeWithIdempotency( + 'gmail', + `${webhookData.id}:${email.id}`, + async () => { + const headers: Record = {} + const payload = email.payload as Record | undefined + if (payload?.headers && Array.isArray(payload.headers)) { + for (const header of payload.headers as { name: string; value: string }[]) { + headers[header.name.toLowerCase()] = header.value + } + } + + let textContent = '' + let htmlContent = '' + + const extractContent = (part: Record) => { + if (!part) return + + if (part.mimeType === 'text/plain') { + const body = part.body as { data?: string } | undefined + if (body?.data) { + textContent = Buffer.from(body.data, 'base64').toString('utf-8') + } + } else if (part.mimeType === 'text/html') { + const body = part.body as { data?: string } | undefined + if (body?.data) { + htmlContent = Buffer.from(body.data, 'base64').toString('utf-8') + } + } + + if (part.parts && Array.isArray(part.parts)) { + for (const subPart of part.parts) { + extractContent(subPart as Record) + } + } + } + + if (payload) { + extractContent(payload) + } + + let date: string | null = null + if (headers.date) { + try { + date = new Date(headers.date).toISOString() + } catch (_e) { + // Keep date as null if parsing fails + } + } else if (email.internalDate) { + date = new Date(Number.parseInt(email.internalDate)).toISOString() + } + + let attachments: GmailAttachment[] = [] + const hasAttachments = payload ? extractAttachmentInfo(payload).length > 0 : false + + if (config.includeAttachments && hasAttachments && payload) { + try { + const attachmentInfo = extractAttachmentInfo(payload) + attachments = await downloadAttachments(email.id, attachmentInfo, accessToken) + } catch (error) { + logger.error( + `[${requestId}] Error downloading attachments for email ${email.id}:`, + error + ) + } + } + + const simplifiedEmail: SimplifiedEmail = { + id: email.id, + threadId: email.threadId, + subject: headers.subject || '[No Subject]', + from: headers.from || '', + to: headers.to || '', + cc: headers.cc || '', + date, + bodyText: textContent, + bodyHtml: htmlContent, + labels: email.labelIds || [], + hasAttachments, + attachments, + } + + const webhookPayload: GmailWebhookPayload = { + email: simplifiedEmail, + timestamp: new Date().toISOString(), + ...(config.includeRawEmail ? { rawEmail: email } : {}), + } + + const result = await processPolledWebhookEvent( + webhookData, + workflowData, + webhookPayload, + requestId + ) + + if (!result.success) { + logger.error( + `[${requestId}] Failed to process webhook for email ${email.id}:`, + result.statusCode, + result.error + ) + throw new Error(`Webhook processing failed (${result.statusCode}): ${result.error}`) + } + + if (config.markAsRead) { + await markEmailAsRead(accessToken, email.id, logger) + } + + return { emailId: email.id, processed: true } + } + ) + + logger.info( + `[${requestId}] Successfully processed email ${email.id} for webhook ${webhookData.id}` + ) + processedCount++ + } catch (error) { + const errorMessage = error instanceof Error ? error.message : 'Unknown error' + logger.error(`[${requestId}] Error processing email ${email.id}:`, errorMessage) + failedCount++ + } + } + + return { processedCount, failedCount } +} + +async function markEmailAsRead( + accessToken: string, + messageId: string, + logger: ReturnType +) { + const modifyUrl = `https://gmail.googleapis.com/gmail/v1/users/me/messages/${messageId}/modify` + + try { + const response = await fetch(modifyUrl, { + method: 'POST', + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ removeLabelIds: ['UNREAD'] }), + }) + + if (!response.ok) { + await response.body?.cancel().catch(() => {}) + throw new Error( + `Failed to mark email ${messageId} as read: ${response.status} ${response.statusText}` + ) + } + } catch (error) { + logger.error(`Error marking email ${messageId} as read:`, error) + throw error + } +} diff --git a/apps/sim/lib/webhooks/imap-polling-service.ts b/apps/sim/lib/webhooks/polling/imap.ts similarity index 57% rename from apps/sim/lib/webhooks/imap-polling-service.ts rename to apps/sim/lib/webhooks/polling/imap.ts index 73219f72353..e5822aa8882 100644 --- a/apps/sim/lib/webhooks/imap-polling-service.ts +++ b/apps/sim/lib/webhooks/polling/imap.ts @@ -1,19 +1,14 @@ -import { db } from '@sim/db' -import { webhook, workflow, workflowDeploymentVersion } from '@sim/db/schema' -import { createLogger } from '@sim/logger' -import type { InferSelectModel } from 'drizzle-orm' -import { and, eq, isNull, or, sql } from 'drizzle-orm' import type { FetchMessageObject, MailboxLockObject } from 'imapflow' import { ImapFlow } from 'imapflow' import { pollingIdempotency } from '@/lib/core/idempotency/service' import { validateDatabaseHost } from '@/lib/core/security/input-validation.server' -import { getInternalApiBaseUrl } from '@/lib/core/utils/urls' -import { generateShortId } from '@/lib/core/utils/uuid' -import { MAX_CONSECUTIVE_FAILURES } from '@/triggers/constants' - -const logger = createLogger('ImapPollingService') - -type WebhookRecord = InferSelectModel +import type { PollingProviderHandler, PollWebhookContext } from '@/lib/webhooks/polling/types' +import { + markWebhookFailed, + markWebhookSuccess, + updateWebhookProviderConfig, +} from '@/lib/webhooks/polling/utils' +import { processPolledWebhookEvent } from '@/lib/webhooks/processor' interface ImapWebhookConfig { host: string @@ -21,13 +16,13 @@ interface ImapWebhookConfig { secure: boolean username: string password: string - mailbox: string | string[] // Can be single mailbox or array of mailboxes + mailbox: string | string[] searchCriteria: string markAsRead: boolean includeAttachments: boolean lastProcessedUid?: number - lastProcessedUidByMailbox?: Record // Track UID per mailbox for multi-mailbox - lastCheckedTimestamp?: string // ISO timestamp of last successful poll + lastProcessedUidByMailbox?: Record + lastCheckedTimestamp?: string maxEmailsPerPoll?: number } @@ -69,206 +64,112 @@ export interface ImapWebhookPayload { timestamp: string } -async function markWebhookFailed(webhookId: string) { - try { - const result = await db - .update(webhook) - .set({ - failedCount: sql`COALESCE(${webhook.failedCount}, 0) + 1`, - lastFailedAt: new Date(), - updatedAt: new Date(), - }) - .where(eq(webhook.id, webhookId)) - .returning({ failedCount: webhook.failedCount }) - - const newFailedCount = result[0]?.failedCount || 0 - const shouldDisable = newFailedCount >= MAX_CONSECUTIVE_FAILURES - - if (shouldDisable) { - await db - .update(webhook) - .set({ - isActive: false, - updatedAt: new Date(), - }) - .where(eq(webhook.id, webhookId)) - - logger.warn( - `Webhook ${webhookId} auto-disabled after ${MAX_CONSECUTIVE_FAILURES} consecutive failures` - ) - } - } catch (err) { - logger.error(`Failed to mark webhook ${webhookId} as failed:`, err) - } -} - -async function markWebhookSuccess(webhookId: string) { - try { - await db - .update(webhook) - .set({ - failedCount: 0, - updatedAt: new Date(), - }) - .where(eq(webhook.id, webhookId)) - } catch (err) { - logger.error(`Failed to mark webhook ${webhookId} as successful:`, err) - } -} - -export async function pollImapWebhooks() { - logger.info('Starting IMAP webhook polling') - - try { - const activeWebhooksResult = await db - .select({ webhook }) - .from(webhook) - .innerJoin(workflow, eq(webhook.workflowId, workflow.id)) - .leftJoin( - workflowDeploymentVersion, - and( - eq(workflowDeploymentVersion.workflowId, workflow.id), - eq(workflowDeploymentVersion.isActive, true) - ) - ) - .where( - and( - eq(webhook.provider, 'imap'), - eq(webhook.isActive, true), - eq(workflow.isDeployed, true), - or( - eq(webhook.deploymentVersionId, workflowDeploymentVersion.id), - and(isNull(workflowDeploymentVersion.id), isNull(webhook.deploymentVersionId)) - ) - ) - ) - - const activeWebhooks = activeWebhooksResult.map((r) => r.webhook) - - if (!activeWebhooks.length) { - logger.info('No active IMAP webhooks found') - return { total: 0, successful: 0, failed: 0, details: [] } - } - - logger.info(`Found ${activeWebhooks.length} active IMAP webhooks`) +export const imapPollingHandler: PollingProviderHandler = { + provider: 'imap', + label: 'IMAP', - const CONCURRENCY = 5 + async pollWebhook(ctx: PollWebhookContext): Promise<'success' | 'failure'> { + const { webhookData, workflowData, requestId, logger } = ctx + const webhookId = webhookData.id - const running: Promise[] = [] - let successCount = 0 - let failureCount = 0 + try { + const config = webhookData.providerConfig as unknown as ImapWebhookConfig - const enqueue = async (webhookData: (typeof activeWebhooks)[number]) => { - const webhookId = webhookData.id - const requestId = generateShortId() + if (!config.host || !config.username || !config.password) { + logger.error(`[${requestId}] Missing IMAP credentials for webhook ${webhookId}`) + await markWebhookFailed(webhookId, logger) + return 'failure' + } - try { - const config = webhookData.providerConfig as unknown as ImapWebhookConfig + const hostValidation = await validateDatabaseHost(config.host, 'host') + if (!hostValidation.isValid) { + logger.error( + `[${requestId}] IMAP host validation failed for webhook ${webhookId}: ${hostValidation.error}` + ) + await markWebhookFailed(webhookId, logger) + return 'failure' + } - if (!config.host || !config.username || !config.password) { - logger.error(`[${requestId}] Missing IMAP credentials for webhook ${webhookId}`) - await markWebhookFailed(webhookId) - failureCount++ - return - } + const { emails, latestUidByMailbox } = await fetchNewEmails( + config, + requestId, + hostValidation.resolvedIP!, + logger + ) + const pollTimestamp = new Date().toISOString() - const hostValidation = await validateDatabaseHost(config.host, 'host') - if (!hostValidation.isValid) { - logger.error( - `[${requestId}] IMAP host validation failed for webhook ${webhookId}: ${hostValidation.error}` - ) - await markWebhookFailed(webhookId) - failureCount++ - return - } + if (!emails || !emails.length) { + await updateImapState(webhookId, latestUidByMailbox, pollTimestamp, config, logger) + await markWebhookSuccess(webhookId, logger) + logger.info(`[${requestId}] No new emails found for webhook ${webhookId}`) + return 'success' + } - const fetchResult = await fetchNewEmails(config, requestId, hostValidation.resolvedIP!) - const { emails, latestUidByMailbox } = fetchResult - const pollTimestamp = new Date().toISOString() + logger.info(`[${requestId}] Found ${emails.length} new emails for webhook ${webhookId}`) - if (!emails || !emails.length) { - await updateWebhookLastProcessedUids(webhookId, latestUidByMailbox, pollTimestamp) - await markWebhookSuccess(webhookId) - logger.info(`[${requestId}] No new emails found for webhook ${webhookId}`) - successCount++ - return - } + const { processedCount, failedCount } = await processEmails( + emails, + webhookData, + workflowData, + config, + requestId, + hostValidation.resolvedIP!, + logger + ) - logger.info(`[${requestId}] Found ${emails.length} new emails for webhook ${webhookId}`) + await updateImapState(webhookId, latestUidByMailbox, pollTimestamp, config, logger) - const { processedCount, failedCount: emailFailedCount } = await processEmails( - emails, - webhookData, - config, - requestId, - hostValidation.resolvedIP! + if (failedCount > 0 && processedCount === 0) { + await markWebhookFailed(webhookId, logger) + logger.warn( + `[${requestId}] All ${failedCount} emails failed to process for webhook ${webhookId}` ) - - await updateWebhookLastProcessedUids(webhookId, latestUidByMailbox, pollTimestamp) - - if (emailFailedCount > 0 && processedCount === 0) { - await markWebhookFailed(webhookId) - failureCount++ - logger.warn( - `[${requestId}] All ${emailFailedCount} emails failed to process for webhook ${webhookId}` - ) - } else { - await markWebhookSuccess(webhookId) - successCount++ - logger.info( - `[${requestId}] Successfully processed ${processedCount} emails for webhook ${webhookId}${emailFailedCount > 0 ? ` (${emailFailedCount} failed)` : ''}` - ) - } - } catch (error) { - logger.error(`[${requestId}] Error processing IMAP webhook ${webhookId}:`, error) - await markWebhookFailed(webhookId) - failureCount++ + return 'failure' } - } - - for (const webhookData of activeWebhooks) { - const promise: Promise = enqueue(webhookData) - .catch((err) => { - logger.error('Unexpected error in webhook processing:', err) - failureCount++ - }) - .finally(() => { - // Self-remove from running array when completed - const idx = running.indexOf(promise) - if (idx !== -1) running.splice(idx, 1) - }) - - running.push(promise) - - if (running.length >= CONCURRENCY) { - await Promise.race(running) - } - } - await Promise.allSettled(running) - - const summary = { - total: activeWebhooks.length, - successful: successCount, - failed: failureCount, - details: [], + await markWebhookSuccess(webhookId, logger) + logger.info( + `[${requestId}] Successfully processed ${processedCount} emails for webhook ${webhookId}${failedCount > 0 ? ` (${failedCount} failed)` : ''}` + ) + return 'success' + } catch (error) { + logger.error(`[${requestId}] Error processing IMAP webhook ${webhookId}:`, error) + await markWebhookFailed(webhookId, logger) + return 'failure' } + }, +} - logger.info('IMAP polling completed', { - total: summary.total, - successful: summary.successful, - failed: summary.failed, - }) +async function updateImapState( + webhookId: string, + uidByMailbox: Record, + timestamp: string, + config: ImapWebhookConfig, + logger: ReturnType +) { + const existingUidByMailbox = config.lastProcessedUidByMailbox || {} + const mergedUidByMailbox = { ...existingUidByMailbox } - return summary - } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error' - logger.error('Error in IMAP polling service:', errorMessage) - throw error + for (const [mailbox, uid] of Object.entries(uidByMailbox)) { + mergedUidByMailbox[mailbox] = Math.max(uid, mergedUidByMailbox[mailbox] || 0) } + + await updateWebhookProviderConfig( + webhookId, + { + lastProcessedUidByMailbox: mergedUidByMailbox, + lastCheckedTimestamp: timestamp, + }, + logger + ) } -async function fetchNewEmails(config: ImapWebhookConfig, requestId: string, resolvedIP: string) { +async function fetchNewEmails( + config: ImapWebhookConfig, + requestId: string, + resolvedIP: string, + logger: ReturnType +) { const client = new ImapFlow({ host: resolvedIP, servername: config.host, @@ -278,15 +179,13 @@ async function fetchNewEmails(config: ImapWebhookConfig, requestId: string, reso user: config.username, pass: config.password, }, - tls: { - rejectUnauthorized: true, - }, + tls: { rejectUnauthorized: true }, logger: false, }) const emails: Array<{ uid: number - mailboxPath: string // Track which mailbox this email came from + mailboxPath: string envelope: FetchMessageObject['envelope'] bodyStructure: FetchMessageObject['bodyStructure'] source?: Buffer @@ -305,13 +204,12 @@ async function fetchNewEmails(config: ImapWebhookConfig, requestId: string, reso if (totalEmailsCollected >= maxEmails) break try { - const mailbox = await client.mailboxOpen(mailboxPath) + await client.mailboxOpen(mailboxPath) - // Parse search criteria - expects JSON object from UI - let searchCriteria: any = { unseen: true } + let searchCriteria: Record = { unseen: true } if (config.searchCriteria) { if (typeof config.searchCriteria === 'object') { - searchCriteria = config.searchCriteria + searchCriteria = config.searchCriteria as unknown as Record } else if (typeof config.searchCriteria === 'string') { try { searchCriteria = JSON.parse(config.searchCriteria) @@ -327,15 +225,11 @@ async function fetchNewEmails(config: ImapWebhookConfig, requestId: string, reso searchCriteria = { ...searchCriteria, uid: `${lastUidForMailbox + 1}:*` } } - // Add time-based filtering similar to Gmail - // If lastCheckedTimestamp exists, use it with 1 minute buffer - // If first poll (no timestamp), default to last 24 hours to avoid processing ALL unseen emails if (config.lastCheckedTimestamp) { const lastChecked = new Date(config.lastCheckedTimestamp) const bufferTime = new Date(lastChecked.getTime() - 60000) searchCriteria = { ...searchCriteria, since: bufferTime } } else { - // First poll: only get emails from last 24 hours to avoid overwhelming first run const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000) searchCriteria = { ...searchCriteria, since: oneDayAgo } } @@ -344,15 +238,13 @@ async function fetchNewEmails(config: ImapWebhookConfig, requestId: string, reso try { const searchResult = await client.search(searchCriteria, { uid: true }) messageUids = searchResult === false ? [] : searchResult - } catch (searchError) { + } catch { continue } - if (messageUids.length === 0) { - continue - } + if (messageUids.length === 0) continue - messageUids.sort((a, b) => a - b) // Sort ascending to process oldest first + messageUids.sort((a, b) => a - b) const remainingSlots = maxEmails - totalEmailsCollected const uidsToProcess = messageUids.slice(0, remainingSlots) @@ -365,12 +257,7 @@ async function fetchNewEmails(config: ImapWebhookConfig, requestId: string, reso for await (const msg of client.fetch( uidsToProcess, - { - uid: true, - envelope: true, - bodyStructure: true, - source: true, - }, + { uid: true, envelope: true, bodyStructure: true, source: true }, { uid: true } )) { emails.push({ @@ -388,7 +275,6 @@ async function fetchNewEmails(config: ImapWebhookConfig, requestId: string, reso } await client.logout() - return { emails, latestUidByMailbox } } catch (error) { try { @@ -400,9 +286,6 @@ async function fetchNewEmails(config: ImapWebhookConfig, requestId: string, reso } } -/** - * Get the list of mailboxes to check based on config - */ function getMailboxesToCheck(config: ImapWebhookConfig): string[] { if (!config.mailbox || (Array.isArray(config.mailbox) && config.mailbox.length === 0)) { return ['INBOX'] @@ -488,7 +371,6 @@ function extractAttachmentsFromSource( bodyStructure: FetchMessageObject['bodyStructure'] ): ImapAttachment[] { const attachments: ImapAttachment[] = [] - if (!bodyStructure) return attachments const content = source.toString('utf-8') @@ -534,24 +416,13 @@ function extractAttachmentsFromSource( return attachments } -/** - * Checks if a body structure contains attachments by examining disposition - */ function hasAttachmentsInBodyStructure(structure: FetchMessageObject['bodyStructure']): boolean { if (!structure) return false - - if (structure.disposition === 'attachment') { - return true - } - - if (structure.disposition === 'inline' && structure.dispositionParameters?.filename) { - return true - } - + if (structure.disposition === 'attachment') return true + if (structure.disposition === 'inline' && structure.dispositionParameters?.filename) return true if (structure.childNodes && Array.isArray(structure.childNodes)) { return structure.childNodes.some((child) => hasAttachmentsInBodyStructure(child)) } - return false } @@ -563,10 +434,12 @@ async function processEmails( bodyStructure: FetchMessageObject['bodyStructure'] source?: Buffer }>, - webhookData: WebhookRecord, + webhookData: PollWebhookContext['webhookData'], + workflowData: PollWebhookContext['workflowData'], config: ImapWebhookConfig, requestId: string, - resolvedIP: string + resolvedIP: string, + logger: ReturnType ) { let processedCount = 0 let failedCount = 0 @@ -580,9 +453,7 @@ async function processEmails( user: config.username, pass: config.password, }, - tls: { - rejectUnauthorized: true, - }, + tls: { rejectUnauthorized: true }, logger: false, }) @@ -644,25 +515,20 @@ async function processEmails( timestamp: new Date().toISOString(), } - const webhookUrl = `${getInternalApiBaseUrl()}/api/webhooks/trigger/${webhookData.path}` - - const response = await fetch(webhookUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'User-Agent': 'Sim/1.0', - }, - body: JSON.stringify(payload), - }) + const result = await processPolledWebhookEvent( + webhookData, + workflowData, + payload, + requestId + ) - if (!response.ok) { - const errorText = await response.text() + if (!result.success) { logger.error( - `[${requestId}] Failed to trigger webhook for email ${email.uid}:`, - response.status, - errorText + `[${requestId}] Failed to process webhook for email ${email.uid}:`, + result.statusCode, + result.error ) - throw new Error(`Webhook request failed: ${response.status} - ${errorText}`) + throw new Error(`Webhook processing failed (${result.statusCode}): ${result.error}`) } if (config.markAsRead) { @@ -684,11 +550,7 @@ async function processEmails( } } - return { - emailUid: email.uid, - webhookStatus: response.status, - processed: true, - } + return { emailUid: email.uid, processed: true } } ) @@ -717,31 +579,3 @@ async function processEmails( return { processedCount, failedCount } } - -async function updateWebhookLastProcessedUids( - webhookId: string, - uidByMailbox: Record, - timestamp: string -) { - const result = await db.select().from(webhook).where(eq(webhook.id, webhookId)) - const existingConfig = (result[0]?.providerConfig as Record) || {} - - const existingUidByMailbox = existingConfig.lastProcessedUidByMailbox || {} - const mergedUidByMailbox = { ...existingUidByMailbox } - - for (const [mailbox, uid] of Object.entries(uidByMailbox)) { - mergedUidByMailbox[mailbox] = Math.max(uid, mergedUidByMailbox[mailbox] || 0) - } - - await db - .update(webhook) - .set({ - providerConfig: { - ...existingConfig, - lastProcessedUidByMailbox: mergedUidByMailbox, - lastCheckedTimestamp: timestamp, - } as any, - updatedAt: new Date(), - }) - .where(eq(webhook.id, webhookId)) -} diff --git a/apps/sim/lib/webhooks/polling/index.ts b/apps/sim/lib/webhooks/polling/index.ts new file mode 100644 index 00000000000..4d0ba2df0de --- /dev/null +++ b/apps/sim/lib/webhooks/polling/index.ts @@ -0,0 +1,9 @@ +export { pollProvider } from '@/lib/webhooks/polling/orchestrator' +export { getPollingHandler, VALID_POLLING_PROVIDERS } from '@/lib/webhooks/polling/registry' +export type { + PollingProviderHandler, + PollSummary, + PollWebhookContext, + WebhookRecord, + WorkflowRecord, +} from '@/lib/webhooks/polling/types' diff --git a/apps/sim/lib/webhooks/polling/orchestrator.ts b/apps/sim/lib/webhooks/polling/orchestrator.ts new file mode 100644 index 00000000000..508ff91972f --- /dev/null +++ b/apps/sim/lib/webhooks/polling/orchestrator.ts @@ -0,0 +1,46 @@ +import { createLogger } from '@sim/logger' +import { generateShortId } from '@/lib/core/utils/uuid' +import { getPollingHandler } from '@/lib/webhooks/polling/registry' +import type { PollSummary, WebhookRecord, WorkflowRecord } from '@/lib/webhooks/polling/types' +import { fetchActiveWebhooks, runWithConcurrency } from '@/lib/webhooks/polling/utils' + +/** Poll all active webhooks for a given provider. */ +export async function pollProvider(providerName: string): Promise { + const handler = getPollingHandler(providerName) + if (!handler) { + throw new Error(`Unknown polling provider: ${providerName}`) + } + + const logger = createLogger(`${handler.label}PollingService`) + logger.info(`Starting ${handler.label} webhook polling`) + + const activeWebhooks = await fetchActiveWebhooks(handler.provider) + if (!activeWebhooks.length) { + logger.info(`No active ${handler.label} webhooks found`) + return { total: 0, successful: 0, failed: 0 } + } + + logger.info(`Found ${activeWebhooks.length} active ${handler.label} webhooks`) + + const { successCount, failureCount } = await runWithConcurrency( + activeWebhooks, + async (entry) => { + const requestId = generateShortId() + return handler.pollWebhook({ + webhookData: entry.webhook as WebhookRecord, + workflowData: entry.workflow as WorkflowRecord, + requestId, + logger, + }) + }, + logger + ) + + const summary: PollSummary = { + total: activeWebhooks.length, + successful: successCount, + failed: failureCount, + } + logger.info(`${handler.label} polling completed`, summary) + return summary +} diff --git a/apps/sim/lib/webhooks/outlook-polling-service.ts b/apps/sim/lib/webhooks/polling/outlook.ts similarity index 50% rename from apps/sim/lib/webhooks/outlook-polling-service.ts rename to apps/sim/lib/webhooks/polling/outlook.ts index 7cf87f1707c..e6874940c61 100644 --- a/apps/sim/lib/webhooks/outlook-polling-service.ts +++ b/apps/sim/lib/webhooks/polling/outlook.ts @@ -1,77 +1,17 @@ -import { db } from '@sim/db' -import { - account, - credentialSet, - webhook, - workflow, - workflowDeploymentVersion, -} from '@sim/db/schema' -import { createLogger } from '@sim/logger' -import { and, eq, isNull, or, sql } from 'drizzle-orm' import { htmlToText } from 'html-to-text' -import { isOrganizationOnTeamOrEnterprisePlan } from '@/lib/billing' -import { pollingIdempotency } from '@/lib/core/idempotency' -import { getInternalApiBaseUrl } from '@/lib/core/utils/urls' -import { generateShortId } from '@/lib/core/utils/uuid' +import { pollingIdempotency } from '@/lib/core/idempotency/service' +import type { PollingProviderHandler, PollWebhookContext } from '@/lib/webhooks/polling/types' import { - getOAuthToken, - refreshAccessTokenIfNeeded, - resolveOAuthAccountId, -} from '@/app/api/auth/oauth/utils' -import { MAX_CONSECUTIVE_FAILURES } from '@/triggers/constants' - -const logger = createLogger('OutlookPollingService') - -async function markWebhookFailed(webhookId: string) { - try { - const result = await db - .update(webhook) - .set({ - failedCount: sql`COALESCE(${webhook.failedCount}, 0) + 1`, - lastFailedAt: new Date(), - updatedAt: new Date(), - }) - .where(eq(webhook.id, webhookId)) - .returning({ failedCount: webhook.failedCount }) - - const newFailedCount = result[0]?.failedCount || 0 - const shouldDisable = newFailedCount >= MAX_CONSECUTIVE_FAILURES - - if (shouldDisable) { - await db - .update(webhook) - .set({ - isActive: false, - updatedAt: new Date(), - }) - .where(eq(webhook.id, webhookId)) - - logger.warn( - `Webhook ${webhookId} auto-disabled after ${MAX_CONSECUTIVE_FAILURES} consecutive failures` - ) - } - } catch (err) { - logger.error(`Failed to mark webhook ${webhookId} as failed:`, err) - } -} - -async function markWebhookSuccess(webhookId: string) { - try { - await db - .update(webhook) - .set({ - failedCount: 0, // Reset on success - updatedAt: new Date(), - }) - .where(eq(webhook.id, webhookId)) - } catch (err) { - logger.error(`Failed to mark webhook ${webhookId} as successful:`, err) - } -} + markWebhookFailed, + markWebhookSuccess, + resolveOAuthCredential, + updateWebhookProviderConfig, +} from '@/lib/webhooks/polling/utils' +import { processPolledWebhookEvent } from '@/lib/webhooks/processor' interface OutlookWebhookConfig { credentialId: string - folderIds?: string[] // e.g., ['inbox', 'sent'] + folderIds?: string[] folderFilterBehavior?: 'INCLUDE' | 'EXCLUDE' markAsRead?: boolean maxEmailsPerPoll?: number @@ -145,10 +85,6 @@ export interface OutlookWebhookPayload { rawEmail?: OutlookEmail } -/** - * Convert HTML content to a readable plain-text representation. - * Keeps reasonable newlines and decodes common HTML entities. - */ function convertHtmlToPlainText(html: string): string { if (!html) return '' return htmlToText(html, { @@ -163,217 +99,78 @@ function convertHtmlToPlainText(html: string): string { }) } -export async function pollOutlookWebhooks() { - logger.info('Starting Outlook webhook polling') +export const outlookPollingHandler: PollingProviderHandler = { + provider: 'outlook', + label: 'Outlook', - try { - const activeWebhooksResult = await db - .select({ webhook }) - .from(webhook) - .innerJoin(workflow, eq(webhook.workflowId, workflow.id)) - .leftJoin( - workflowDeploymentVersion, - and( - eq(workflowDeploymentVersion.workflowId, workflow.id), - eq(workflowDeploymentVersion.isActive, true) - ) - ) - .where( - and( - eq(webhook.provider, 'outlook'), - eq(webhook.isActive, true), - eq(workflow.isDeployed, true), - or( - eq(webhook.deploymentVersionId, workflowDeploymentVersion.id), - and(isNull(workflowDeploymentVersion.id), isNull(webhook.deploymentVersionId)) - ) - ) - ) - - const activeWebhooks = activeWebhooksResult.map((r) => r.webhook) + async pollWebhook(ctx: PollWebhookContext): Promise<'success' | 'failure'> { + const { webhookData, workflowData, requestId, logger } = ctx + const webhookId = webhookData.id - if (!activeWebhooks.length) { - logger.info('No active Outlook webhooks found') - return { total: 0, successful: 0, failed: 0, details: [] } - } - - logger.info(`Found ${activeWebhooks.length} active Outlook webhooks`) - - const CONCURRENCY = 10 - const running: Promise[] = [] - let successCount = 0 - let failureCount = 0 - - const enqueue = async (webhookData: (typeof activeWebhooks)[number]) => { - const webhookId = webhookData.id - const requestId = generateShortId() - - try { - logger.info(`[${requestId}] Processing Outlook webhook: ${webhookId}`) - - const metadata = webhookData.providerConfig as any - const credentialId: string | undefined = metadata?.credentialId - const userId: string | undefined = metadata?.userId - const credentialSetId: string | undefined = webhookData.credentialSetId ?? undefined - - if (!credentialId && !userId) { - logger.error(`[${requestId}] Missing credentialId and userId for webhook ${webhookId}`) - await markWebhookFailed(webhookId) - failureCount++ - return - } - - if (credentialSetId) { - const [cs] = await db - .select({ organizationId: credentialSet.organizationId }) - .from(credentialSet) - .where(eq(credentialSet.id, credentialSetId)) - .limit(1) - - if (cs?.organizationId) { - const hasAccess = await isOrganizationOnTeamOrEnterprisePlan(cs.organizationId) - if (!hasAccess) { - logger.error( - `[${requestId}] Polling Group plan restriction: Your current plan does not support Polling Groups. Upgrade to Team or Enterprise to use this feature.`, - { - webhookId, - credentialSetId, - organizationId: cs.organizationId, - } - ) - await markWebhookFailed(webhookId) - failureCount++ - return - } - } - } - - let accessToken: string | null = null - if (credentialId) { - const resolved = await resolveOAuthAccountId(credentialId) - if (!resolved) { - logger.error( - `[${requestId}] Failed to resolve OAuth account for credential ${credentialId}, webhook ${webhookId}` - ) - await markWebhookFailed(webhookId) - failureCount++ - return - } - const rows = await db - .select() - .from(account) - .where(eq(account.id, resolved.accountId)) - .limit(1) - if (!rows.length) { - logger.error( - `[${requestId}] Credential ${credentialId} not found for webhook ${webhookId}` - ) - await markWebhookFailed(webhookId) - failureCount++ - return - } - const ownerUserId = rows[0].userId - accessToken = await refreshAccessTokenIfNeeded(resolved.accountId, ownerUserId, requestId) - } else if (userId) { - accessToken = await getOAuthToken(userId, 'outlook') - } - - if (!accessToken) { - logger.error( - `[${requestId}] Failed to get Outlook access token for webhook ${webhookId} (cred or fallback)` - ) - await markWebhookFailed(webhookId) - failureCount++ - return - } + try { + logger.info(`[${requestId}] Processing Outlook webhook: ${webhookId}`) - const config = webhookData.providerConfig as unknown as OutlookWebhookConfig + const accessToken = await resolveOAuthCredential(webhookData, 'outlook', requestId, logger) + const config = webhookData.providerConfig as unknown as OutlookWebhookConfig + const now = new Date() - const now = new Date() + const { emails } = await fetchNewOutlookEmails(accessToken, config, requestId, logger) - const fetchResult = await fetchNewOutlookEmails(accessToken, config, requestId) - const { emails } = fetchResult + if (!emails || !emails.length) { + await updateWebhookProviderConfig( + webhookId, + { lastCheckedTimestamp: now.toISOString() }, + logger + ) + await markWebhookSuccess(webhookId, logger) + logger.info(`[${requestId}] No new emails found for webhook ${webhookId}`) + return 'success' + } - if (!emails || !emails.length) { - await updateWebhookLastChecked(webhookId, now.toISOString()) - await markWebhookSuccess(webhookId) - logger.info(`[${requestId}] No new emails found for webhook ${webhookId}`) - successCount++ - return - } + logger.info(`[${requestId}] Found ${emails.length} emails for webhook ${webhookId}`) - logger.info(`[${requestId}] Found ${emails.length} emails for webhook ${webhookId}`) + const { processedCount, failedCount } = await processOutlookEmails( + emails, + webhookData, + workflowData, + config, + accessToken, + requestId, + logger + ) - logger.info(`[${requestId}] Processing ${emails.length} emails for webhook ${webhookId}`) + await updateWebhookProviderConfig( + webhookId, + { lastCheckedTimestamp: now.toISOString() }, + logger + ) - const { processedCount, failedCount } = await processOutlookEmails( - emails, - webhookData, - config, - accessToken, - requestId + if (failedCount > 0 && processedCount === 0) { + await markWebhookFailed(webhookId, logger) + logger.warn( + `[${requestId}] All ${failedCount} emails failed to process for webhook ${webhookId}` ) - - await updateWebhookLastChecked(webhookId, now.toISOString()) - - if (failedCount > 0 && processedCount === 0) { - await markWebhookFailed(webhookId) - failureCount++ - logger.warn( - `[${requestId}] All ${failedCount} emails failed to process for webhook ${webhookId}` - ) - } else { - await markWebhookSuccess(webhookId) - successCount++ - logger.info( - `[${requestId}] Successfully processed ${processedCount} emails for webhook ${webhookId}${failedCount > 0 ? ` (${failedCount} failed)` : ''}` - ) - } - } catch (error) { - logger.error(`[${requestId}] Error processing Outlook webhook ${webhookId}:`, error) - await markWebhookFailed(webhookId) - failureCount++ + return 'failure' } - } - - for (const webhookData of activeWebhooks) { - const promise: Promise = enqueue(webhookData) - .catch((err) => { - logger.error('Unexpected error in webhook processing:', err) - failureCount++ - }) - .finally(() => { - const idx = running.indexOf(promise) - if (idx !== -1) running.splice(idx, 1) - }) - - running.push(promise) - - if (running.length >= CONCURRENCY) { - await Promise.race(running) - } - } - await Promise.allSettled(running) - - logger.info(`Outlook polling completed: ${successCount} successful, ${failureCount} failed`) - - return { - total: activeWebhooks.length, - successful: successCount, - failed: failureCount, - details: [], + await markWebhookSuccess(webhookId, logger) + logger.info( + `[${requestId}] Successfully processed ${processedCount} emails for webhook ${webhookId}${failedCount > 0 ? ` (${failedCount} failed)` : ''}` + ) + return 'success' + } catch (error) { + logger.error(`[${requestId}] Error processing Outlook webhook ${webhookId}:`, error) + await markWebhookFailed(webhookId, logger) + return 'failure' } - } catch (error) { - logger.error('Error during Outlook webhook polling:', error) - throw error - } + }, } async function fetchNewOutlookEmails( accessToken: string, config: OutlookWebhookConfig, - requestId: string + requestId: string, + logger: ReturnType ) { try { const apiUrl = 'https://graph.microsoft.com/v1.0/me/messages' @@ -383,9 +180,7 @@ async function fetchNewOutlookEmails( '$select', 'id,conversationId,subject,bodyPreview,body,from,toRecipients,ccRecipients,receivedDateTime,sentDateTime,hasAttachments,isRead,parentFolderId' ) - params.append('$orderby', 'receivedDateTime desc') - params.append('$top', (config.maxEmailsPerPoll || 25).toString()) if (config.lastCheckedTimestamp) { @@ -395,7 +190,6 @@ async function fetchNewOutlookEmails( } const fullUrl = `${apiUrl}?${params.toString()}` - logger.info(`[${requestId}] Fetching emails from: ${fullUrl}`) const response = await fetch(fullUrl, { @@ -427,7 +221,8 @@ async function fetchNewOutlookEmails( resolvedFolderIds = await resolveWellKnownFolderIds( accessToken, config.folderIds, - requestId + requestId, + logger ) } } @@ -463,7 +258,8 @@ function isWellKnownFolderName(folderId: string): boolean { async function resolveWellKnownFolderId( accessToken: string, folderName: string, - requestId: string + requestId: string, + logger: ReturnType ): Promise { try { const response = await fetch(`https://graph.microsoft.com/v1.0/me/mailFolders/${folderName}`, { @@ -491,18 +287,16 @@ async function resolveWellKnownFolderId( async function resolveWellKnownFolderIds( accessToken: string, folderIds: string[], - requestId: string + requestId: string, + logger: ReturnType ): Promise> { const resolvedIds = new Map() - const wellKnownFolders = folderIds.filter(isWellKnownFolderName) - if (wellKnownFolders.length === 0) { - return resolvedIds - } + if (wellKnownFolders.length === 0) return resolvedIds const resolutions = await Promise.all( wellKnownFolders.map(async (folderName) => { - const actualId = await resolveWellKnownFolderId(accessToken, folderName, requestId) + const actualId = await resolveWellKnownFolderId(accessToken, folderName, requestId, logger) return { folderName, actualId } }) ) @@ -516,7 +310,6 @@ async function resolveWellKnownFolderIds( logger.info( `[${requestId}] Resolved ${resolvedIds.size}/${wellKnownFolders.length} well-known folders` ) - return resolvedIds } @@ -525,16 +318,12 @@ function filterEmailsByFolder( config: OutlookWebhookConfig, resolvedFolderIds?: Map ): OutlookEmail[] { - if (!config.folderIds || !config.folderIds.length) { - return emails - } + if (!config.folderIds || !config.folderIds.length) return emails const actualFolderIds = config.folderIds.map((configFolder) => { if (resolvedFolderIds && isWellKnownFolderName(configFolder)) { const resolvedId = resolvedFolderIds.get(configFolder.toLowerCase()) - if (resolvedId) { - return resolvedId - } + if (resolvedId) return resolvedId } return configFolder }) @@ -544,17 +333,18 @@ function filterEmailsByFolder( const hasMatchingFolder = actualFolderIds.some( (folderId) => emailFolderId.toLowerCase() === folderId.toLowerCase() ) - return config.folderFilterBehavior === 'INCLUDE' ? hasMatchingFolder : !hasMatchingFolder }) } async function processOutlookEmails( emails: OutlookEmail[], - webhookData: any, + webhookData: PollWebhookContext['webhookData'], + workflowData: PollWebhookContext['workflowData'], config: OutlookWebhookConfig, accessToken: string, - requestId: string + requestId: string, + logger: ReturnType ) { let processedCount = 0 let failedCount = 0 @@ -568,7 +358,12 @@ async function processOutlookEmails( let attachments: OutlookAttachment[] = [] if (config.includeAttachments && email.hasAttachments) { try { - attachments = await downloadOutlookAttachments(accessToken, email.id, requestId) + attachments = await downloadOutlookAttachments( + accessToken, + email.id, + requestId, + logger + ) } catch (error) { logger.error( `[${requestId}] Error downloading attachments for email ${email.id}:`, @@ -588,12 +383,8 @@ async function processOutlookEmails( bodyText: (() => { const content = email.body?.content || '' const type = (email.body?.contentType || '').toLowerCase() - if (!content) { - return email.bodyPreview || '' - } - if (type === 'text' || type === 'text/plain') { - return content - } + if (!content) return email.bodyPreview || '' + if (type === 'text' || type === 'text/plain') return content return convertHtmlToPlainText(content) })(), bodyHtml: email.body?.content || '', @@ -618,36 +409,27 @@ async function processOutlookEmails( `[${requestId}] Processing email: ${email.subject} from ${email.from?.emailAddress?.address}` ) - const webhookUrl = `${getInternalApiBaseUrl()}/api/webhooks/trigger/${webhookData.path}` - - const response = await fetch(webhookUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'User-Agent': 'Sim/1.0', - }, - body: JSON.stringify(payload), - }) + const result = await processPolledWebhookEvent( + webhookData, + workflowData, + payload, + requestId + ) - if (!response.ok) { - const errorText = await response.text() + if (!result.success) { logger.error( - `[${requestId}] Failed to trigger webhook for email ${email.id}:`, - response.status, - errorText + `[${requestId}] Failed to process webhook for email ${email.id}:`, + result.statusCode, + result.error ) - throw new Error(`Webhook request failed: ${response.status} - ${errorText}`) + throw new Error(`Webhook processing failed (${result.statusCode}): ${result.error}`) } if (config.markAsRead) { - await markOutlookEmailAsRead(accessToken, email.id) + await markOutlookEmailAsRead(accessToken, email.id, logger) } - return { - emailId: email.id, - webhookStatus: response.status, - processed: true, - } + return { emailId: email.id, processed: true } } ) @@ -667,7 +449,8 @@ async function processOutlookEmails( async function downloadOutlookAttachments( accessToken: string, messageId: string, - requestId: string + requestId: string, + logger: ReturnType ): Promise { const attachments: OutlookAttachment[] = [] @@ -722,7 +505,11 @@ async function downloadOutlookAttachments( return attachments } -async function markOutlookEmailAsRead(accessToken: string, messageId: string) { +async function markOutlookEmailAsRead( + accessToken: string, + messageId: string, + logger: ReturnType +) { try { const response = await fetch(`https://graph.microsoft.com/v1.0/me/messages/${messageId}`, { method: 'PATCH', @@ -730,9 +517,7 @@ async function markOutlookEmailAsRead(accessToken: string, messageId: string) { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json', }, - body: JSON.stringify({ - isRead: true, - }), + body: JSON.stringify({ isRead: true }), }) if (!response.ok) { @@ -746,34 +531,3 @@ async function markOutlookEmailAsRead(accessToken: string, messageId: string) { logger.error(`Error marking email ${messageId} as read:`, error) } } - -async function updateWebhookLastChecked(webhookId: string, timestamp: string) { - try { - const currentWebhook = await db - .select({ providerConfig: webhook.providerConfig }) - .from(webhook) - .where(eq(webhook.id, webhookId)) - .limit(1) - - if (!currentWebhook.length) { - logger.error(`Webhook ${webhookId} not found`) - return - } - - const currentConfig = (currentWebhook[0].providerConfig as any) || {} - const updatedConfig = { - ...currentConfig, - lastCheckedTimestamp: timestamp, - } - - await db - .update(webhook) - .set({ - providerConfig: updatedConfig, - updatedAt: new Date(), - }) - .where(eq(webhook.id, webhookId)) - } catch (error) { - logger.error(`Error updating webhook ${webhookId} last checked timestamp:`, error) - } -} diff --git a/apps/sim/lib/webhooks/polling/registry.ts b/apps/sim/lib/webhooks/polling/registry.ts new file mode 100644 index 00000000000..fe2db69ed4f --- /dev/null +++ b/apps/sim/lib/webhooks/polling/registry.ts @@ -0,0 +1,19 @@ +import { gmailPollingHandler } from '@/lib/webhooks/polling/gmail' +import { imapPollingHandler } from '@/lib/webhooks/polling/imap' +import { outlookPollingHandler } from '@/lib/webhooks/polling/outlook' +import { rssPollingHandler } from '@/lib/webhooks/polling/rss' +import type { PollingProviderHandler } from '@/lib/webhooks/polling/types' + +const POLLING_HANDLERS: Record = { + gmail: gmailPollingHandler, + imap: imapPollingHandler, + outlook: outlookPollingHandler, + rss: rssPollingHandler, +} + +export const VALID_POLLING_PROVIDERS = new Set(Object.keys(POLLING_HANDLERS)) + +/** Look up the polling handler for a provider. */ +export function getPollingHandler(provider: string): PollingProviderHandler | undefined { + return POLLING_HANDLERS[provider] +} diff --git a/apps/sim/lib/webhooks/polling/rss.ts b/apps/sim/lib/webhooks/polling/rss.ts new file mode 100644 index 00000000000..31044fc8924 --- /dev/null +++ b/apps/sim/lib/webhooks/polling/rss.ts @@ -0,0 +1,307 @@ +import Parser from 'rss-parser' +import { pollingIdempotency } from '@/lib/core/idempotency/service' +import { + secureFetchWithPinnedIP, + validateUrlWithDNS, +} from '@/lib/core/security/input-validation.server' +import type { PollingProviderHandler, PollWebhookContext } from '@/lib/webhooks/polling/types' +import { + markWebhookFailed, + markWebhookSuccess, + updateWebhookProviderConfig, +} from '@/lib/webhooks/polling/utils' +import { processPolledWebhookEvent } from '@/lib/webhooks/processor' + +const MAX_GUIDS_TO_TRACK = 100 + +interface RssWebhookConfig { + feedUrl: string + lastCheckedTimestamp?: string + lastSeenGuids?: string[] + etag?: string + lastModified?: string +} + +interface RssItem { + title?: string + link?: string + pubDate?: string + guid?: string + description?: string + content?: string + contentSnippet?: string + author?: string + creator?: string + categories?: string[] + enclosure?: { + url: string + type?: string + length?: string | number + } + isoDate?: string + [key: string]: unknown +} + +interface RssFeed { + title?: string + link?: string + description?: string + items: RssItem[] +} + +export interface RssWebhookPayload { + title?: string + link?: string + pubDate?: string + item: RssItem + feed: { + title?: string + link?: string + description?: string + } + timestamp: string +} + +const parser = new Parser({ + timeout: 30000, + headers: { + 'User-Agent': 'Sim/1.0 RSS Poller', + }, +}) + +export const rssPollingHandler: PollingProviderHandler = { + provider: 'rss', + label: 'RSS', + + async pollWebhook(ctx: PollWebhookContext): Promise<'success' | 'failure'> { + const { webhookData, workflowData, requestId, logger } = ctx + const webhookId = webhookData.id + + try { + const config = webhookData.providerConfig as unknown as RssWebhookConfig + + if (!config?.feedUrl) { + logger.error(`[${requestId}] Missing feedUrl for webhook ${webhookId}`) + await markWebhookFailed(webhookId, logger) + return 'failure' + } + + const now = new Date() + const { feed, items: newItems } = await fetchNewRssItems(config, requestId, logger) + + if (!newItems.length) { + await updateRssState(webhookId, now.toISOString(), [], config, logger) + await markWebhookSuccess(webhookId, logger) + logger.info(`[${requestId}] No new items found for webhook ${webhookId}`) + return 'success' + } + + logger.info(`[${requestId}] Found ${newItems.length} new items for webhook ${webhookId}`) + + const { processedCount, failedCount } = await processRssItems( + newItems, + feed, + webhookData, + workflowData, + requestId, + logger + ) + + const newGuids = newItems + .map((item) => item.guid || item.link || '') + .filter((guid) => guid.length > 0) + + await updateRssState(webhookId, now.toISOString(), newGuids, config, logger) + + if (failedCount > 0 && processedCount === 0) { + await markWebhookFailed(webhookId, logger) + logger.warn( + `[${requestId}] All ${failedCount} items failed to process for webhook ${webhookId}` + ) + return 'failure' + } + + await markWebhookSuccess(webhookId, logger) + logger.info( + `[${requestId}] Successfully processed ${processedCount} items for webhook ${webhookId}${failedCount > 0 ? ` (${failedCount} failed)` : ''}` + ) + return 'success' + } catch (error) { + logger.error(`[${requestId}] Error processing RSS webhook ${webhookId}:`, error) + await markWebhookFailed(webhookId, logger) + return 'failure' + } + }, +} + +async function updateRssState( + webhookId: string, + timestamp: string, + newGuids: string[], + config: RssWebhookConfig, + logger: ReturnType +) { + const existingGuids = config.lastSeenGuids || [] + const allGuids = [...newGuids, ...existingGuids].slice(0, MAX_GUIDS_TO_TRACK) + + await updateWebhookProviderConfig( + webhookId, + { + lastCheckedTimestamp: timestamp, + lastSeenGuids: allGuids, + }, + logger + ) +} + +async function fetchNewRssItems( + config: RssWebhookConfig, + requestId: string, + logger: ReturnType +): Promise<{ feed: RssFeed; items: RssItem[] }> { + try { + const urlValidation = await validateUrlWithDNS(config.feedUrl, 'feedUrl') + if (!urlValidation.isValid) { + logger.error(`[${requestId}] Invalid RSS feed URL: ${urlValidation.error}`) + throw new Error(`Invalid RSS feed URL: ${urlValidation.error}`) + } + + const response = await secureFetchWithPinnedIP(config.feedUrl, urlValidation.resolvedIP!, { + headers: { + 'User-Agent': 'Sim/1.0 RSS Poller', + Accept: 'application/rss+xml, application/xml, text/xml, */*', + }, + timeout: 30000, + }) + + if (!response.ok) { + await response.text().catch(() => {}) + throw new Error(`Failed to fetch RSS feed: ${response.status} ${response.statusText}`) + } + + const xmlContent = await response.text() + const feed = await parser.parseString(xmlContent) + + if (!feed.items || !feed.items.length) { + return { feed: feed as RssFeed, items: [] } + } + + const lastCheckedTime = config.lastCheckedTimestamp + ? new Date(config.lastCheckedTimestamp) + : null + const lastSeenGuids = new Set(config.lastSeenGuids || []) + + const newItems = feed.items.filter((item) => { + const itemGuid = item.guid || item.link || '' + + if (itemGuid && lastSeenGuids.has(itemGuid)) { + return false + } + + if (lastCheckedTime && item.isoDate) { + const itemDate = new Date(item.isoDate) + if (itemDate <= lastCheckedTime) { + return false + } + } + + return true + }) + + newItems.sort((a, b) => { + const dateA = a.isoDate ? new Date(a.isoDate).getTime() : 0 + const dateB = b.isoDate ? new Date(b.isoDate).getTime() : 0 + return dateB - dateA + }) + + const limitedItems = newItems.slice(0, 25) + + logger.info( + `[${requestId}] Found ${newItems.length} new items (processing ${limitedItems.length})` + ) + + return { feed: feed as RssFeed, items: limitedItems as RssItem[] } + } catch (error) { + const errorMessage = error instanceof Error ? error.message : 'Unknown error' + logger.error(`[${requestId}] Error fetching RSS feed:`, errorMessage) + throw error + } +} + +async function processRssItems( + items: RssItem[], + feed: RssFeed, + webhookData: PollWebhookContext['webhookData'], + workflowData: PollWebhookContext['workflowData'], + requestId: string, + logger: ReturnType +): Promise<{ processedCount: number; failedCount: number }> { + let processedCount = 0 + let failedCount = 0 + + for (const item of items) { + try { + const itemGuid = item.guid || item.link || `${item.title}-${item.pubDate}` + + await pollingIdempotency.executeWithIdempotency( + 'rss', + `${webhookData.id}:${itemGuid}`, + async () => { + const payload: RssWebhookPayload = { + title: item.title, + link: item.link, + pubDate: item.pubDate, + item: { + title: item.title, + link: item.link, + pubDate: item.pubDate, + guid: item.guid, + description: item.description, + content: item.content, + contentSnippet: item.contentSnippet, + author: item.author || item.creator, + categories: item.categories, + enclosure: item.enclosure, + isoDate: item.isoDate, + }, + feed: { + title: feed.title, + link: feed.link, + description: feed.description, + }, + timestamp: new Date().toISOString(), + } + + const result = await processPolledWebhookEvent( + webhookData, + workflowData, + payload, + requestId + ) + + if (!result.success) { + logger.error( + `[${requestId}] Failed to process webhook for item ${itemGuid}:`, + result.statusCode, + result.error + ) + throw new Error(`Webhook processing failed (${result.statusCode}): ${result.error}`) + } + + return { itemGuid, processed: true } + } + ) + + logger.info( + `[${requestId}] Successfully processed item ${item.title || itemGuid} for webhook ${webhookData.id}` + ) + processedCount++ + } catch (error) { + const errorMessage = error instanceof Error ? error.message : 'Unknown error' + logger.error(`[${requestId}] Error processing item:`, errorMessage) + failedCount++ + } + } + + return { processedCount, failedCount } +} diff --git a/apps/sim/lib/webhooks/polling/types.ts b/apps/sim/lib/webhooks/polling/types.ts new file mode 100644 index 00000000000..a69b0427f25 --- /dev/null +++ b/apps/sim/lib/webhooks/polling/types.ts @@ -0,0 +1,58 @@ +import type { Logger } from '@sim/logger' + +/** Summary returned after polling all webhooks for a provider. */ +export interface PollSummary { + total: number + successful: number + failed: number +} + +/** Context passed to a provider handler when processing one webhook. */ +export interface PollWebhookContext { + webhookData: WebhookRecord + workflowData: WorkflowRecord + requestId: string + logger: Logger +} + +/** DB row shape for the webhook table. */ +export interface WebhookRecord { + id: string + path: string + provider: string | null + blockId: string | null + providerConfig: unknown + credentialSetId: string | null + workflowId: string + [key: string]: unknown +} + +/** DB row shape for the workflow table. */ +export interface WorkflowRecord { + id: string + userId: string + workspaceId: string + [key: string]: unknown +} + +/** + * Strategy interface for provider-specific polling behavior. + * Mirrors `WebhookProviderHandler` from `providers/types.ts`. + * + * Each provider implements `pollWebhook()` — the full inner loop for one webhook: + * validate config, resolve credentials, fetch new items, process each via + * `processPolledWebhookEvent()` (wrapped in `pollingIdempotency`), update state. + */ +export interface PollingProviderHandler { + /** Provider name used in DB queries (e.g. 'gmail', 'rss'). */ + readonly provider: string + + /** Display label for log messages (e.g. 'Gmail', 'RSS'). */ + readonly label: string + + /** + * Process a single webhook entry. + * Return 'success' (even if 0 new items) or 'failure'. + */ + pollWebhook(ctx: PollWebhookContext): Promise<'success' | 'failure'> +} diff --git a/apps/sim/lib/webhooks/polling/utils.ts b/apps/sim/lib/webhooks/polling/utils.ts new file mode 100644 index 00000000000..cca46585258 --- /dev/null +++ b/apps/sim/lib/webhooks/polling/utils.ts @@ -0,0 +1,242 @@ +import { db } from '@sim/db' +import { + account, + credentialSet, + webhook, + workflow, + workflowDeploymentVersion, +} from '@sim/db/schema' +import type { Logger } from '@sim/logger' +import { and, eq, isNull, or, sql } from 'drizzle-orm' +import { isOrganizationOnTeamOrEnterprisePlan } from '@/lib/billing' +import type { WebhookRecord, WorkflowRecord } from '@/lib/webhooks/polling/types' +import { + getOAuthToken, + refreshAccessTokenIfNeeded, + resolveOAuthAccountId, +} from '@/app/api/auth/oauth/utils' +import { MAX_CONSECUTIVE_FAILURES } from '@/triggers/constants' + +/** Concurrency limit for parallel webhook processing. Standardized across all providers. */ +export const CONCURRENCY = 10 + +/** Increment the webhook's failure count. Auto-disables after MAX_CONSECUTIVE_FAILURES. */ +export async function markWebhookFailed(webhookId: string, logger: Logger): Promise { + try { + const result = await db + .update(webhook) + .set({ + failedCount: sql`COALESCE(${webhook.failedCount}, 0) + 1`, + lastFailedAt: new Date(), + updatedAt: new Date(), + }) + .where(eq(webhook.id, webhookId)) + .returning({ failedCount: webhook.failedCount }) + + const newFailedCount = result[0]?.failedCount || 0 + if (newFailedCount >= MAX_CONSECUTIVE_FAILURES) { + await db + .update(webhook) + .set({ + isActive: false, + updatedAt: new Date(), + }) + .where(eq(webhook.id, webhookId)) + + logger.warn( + `Webhook ${webhookId} auto-disabled after ${MAX_CONSECUTIVE_FAILURES} consecutive failures` + ) + } + } catch (err) { + logger.error(`Failed to mark webhook ${webhookId} as failed:`, err) + } +} + +/** Reset the webhook's failure count on successful poll. */ +export async function markWebhookSuccess(webhookId: string, logger: Logger): Promise { + try { + await db + .update(webhook) + .set({ + failedCount: 0, + updatedAt: new Date(), + }) + .where(eq(webhook.id, webhookId)) + } catch (err) { + logger.error(`Failed to mark webhook ${webhookId} as successful:`, err) + } +} + +/** Fetch all active webhooks for a provider, joined with their workflow. */ +export async function fetchActiveWebhooks( + provider: string +): Promise<{ webhook: WebhookRecord; workflow: WorkflowRecord }[]> { + const rows = await db + .select({ webhook, workflow }) + .from(webhook) + .innerJoin(workflow, eq(webhook.workflowId, workflow.id)) + .leftJoin( + workflowDeploymentVersion, + and( + eq(workflowDeploymentVersion.workflowId, workflow.id), + eq(workflowDeploymentVersion.isActive, true) + ) + ) + .where( + and( + eq(webhook.provider, provider), + eq(webhook.isActive, true), + isNull(webhook.archivedAt), + eq(workflow.isDeployed, true), + isNull(workflow.archivedAt), + or( + eq(webhook.deploymentVersionId, workflowDeploymentVersion.id), + and(isNull(workflowDeploymentVersion.id), isNull(webhook.deploymentVersionId)) + ) + ) + ) + + return rows as unknown as { webhook: WebhookRecord; workflow: WorkflowRecord }[] +} + +/** + * Run an async function over entries with bounded concurrency. + * Returns aggregate success/failure counts. + */ +export async function runWithConcurrency( + entries: { webhook: WebhookRecord; workflow: WorkflowRecord }[], + processFn: (entry: { + webhook: WebhookRecord + workflow: WorkflowRecord + }) => Promise<'success' | 'failure'>, + logger: Logger +): Promise<{ successCount: number; failureCount: number }> { + const running: Promise[] = [] + let successCount = 0 + let failureCount = 0 + + for (const entry of entries) { + const promise: Promise = processFn(entry) + .then((result) => { + if (result === 'success') { + successCount++ + } else { + failureCount++ + } + }) + .catch((err) => { + logger.error('Unexpected error in webhook processing:', err) + failureCount++ + }) + .finally(() => { + const idx = running.indexOf(promise) + if (idx !== -1) running.splice(idx, 1) + }) + + running.push(promise) + + if (running.length >= CONCURRENCY) { + await Promise.race(running) + } + } + + await Promise.allSettled(running) + + return { successCount, failureCount } +} + +/** + * Read-merge-write pattern for updating provider-specific config fields. + * Each provider passes its specific state updates (historyId, lastSeenGuids, etc.). + */ +export async function updateWebhookProviderConfig( + webhookId: string, + configUpdates: Record, + logger: Logger +): Promise { + try { + const result = await db.select().from(webhook).where(eq(webhook.id, webhookId)) + const existingConfig = (result[0]?.providerConfig as Record) || {} + + await db + .update(webhook) + .set({ + providerConfig: { + ...existingConfig, + ...configUpdates, + } as Record, + updatedAt: new Date(), + }) + .where(eq(webhook.id, webhookId)) + } catch (err) { + logger.error(`Failed to update webhook ${webhookId} config:`, err) + } +} + +/** + * Resolve OAuth credentials for a webhook. Shared by Gmail and Outlook. + * Returns the access token or throws on failure. + */ +export async function resolveOAuthCredential( + webhookData: WebhookRecord, + oauthProvider: string, + requestId: string, + logger: Logger +): Promise { + const metadata = webhookData.providerConfig as Record | null + const credentialId = metadata?.credentialId as string | undefined + const userId = metadata?.userId as string | undefined + const credentialSetId = (webhookData.credentialSetId as string | undefined) ?? undefined + + if (!credentialId && !userId) { + throw new Error(`Missing credential info for webhook ${webhookData.id}`) + } + + if (credentialSetId) { + const [cs] = await db + .select({ organizationId: credentialSet.organizationId }) + .from(credentialSet) + .where(eq(credentialSet.id, credentialSetId)) + .limit(1) + + if (cs?.organizationId) { + const hasAccess = await isOrganizationOnTeamOrEnterprisePlan(cs.organizationId) + if (!hasAccess) { + logger.error( + `[${requestId}] Polling Group plan restriction: Your current plan does not support Polling Groups. Upgrade to Team or Enterprise to use this feature.`, + { + webhookId: webhookData.id, + credentialSetId, + organizationId: cs.organizationId, + } + ) + throw new Error('Polling Group plan restriction') + } + } + } + + let accessToken: string | null = null + + if (credentialId) { + const resolved = await resolveOAuthAccountId(credentialId) + if (!resolved) { + throw new Error( + `Failed to resolve OAuth account for credential ${credentialId}, webhook ${webhookData.id}` + ) + } + const rows = await db.select().from(account).where(eq(account.id, resolved.accountId)).limit(1) + if (!rows.length) { + throw new Error(`Credential ${credentialId} not found for webhook ${webhookData.id}`) + } + const ownerUserId = rows[0].userId + accessToken = await refreshAccessTokenIfNeeded(resolved.accountId, ownerUserId, requestId) + } else if (userId) { + accessToken = await getOAuthToken(userId, oauthProvider) + } + + if (!accessToken) { + throw new Error(`Failed to get ${oauthProvider} access token for webhook ${webhookData.id}`) + } + + return accessToken +} diff --git a/apps/sim/lib/webhooks/processor.ts b/apps/sim/lib/webhooks/processor.ts index eb1503c0029..d75f539ee38 100644 --- a/apps/sim/lib/webhooks/processor.ts +++ b/apps/sim/lib/webhooks/processor.ts @@ -4,12 +4,13 @@ import { createLogger } from '@sim/logger' import { and, eq, isNull, or } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { isOrganizationOnTeamOrEnterprisePlan } from '@/lib/billing/core/subscription' +import { tryAdmit } from '@/lib/core/admission/gate' import { getInlineJobQueue, getJobQueue, shouldExecuteInline } from '@/lib/core/async-jobs' import type { AsyncExecutionCorrelation } from '@/lib/core/async-jobs/types' import { createBullMQJobData, isBullMQEnabled } from '@/lib/core/bullmq' import { isProd } from '@/lib/core/config/feature-flags' import { generateId } from '@/lib/core/utils/uuid' -import { enqueueWorkspaceDispatch } from '@/lib/core/workspace-dispatch' +import { DispatchQueueFullError, enqueueWorkspaceDispatch } from '@/lib/core/workspace-dispatch' import { getEffectiveDecryptedEnv } from '@/lib/environment/utils' import { preprocessExecution } from '@/lib/execution/preprocessing' import { @@ -18,6 +19,7 @@ import { requiresPendingWebhookVerification, } from '@/lib/webhooks/pending-verification' import { getProviderHandler } from '@/lib/webhooks/providers' +import { blockExistsInDeployment } from '@/lib/workflows/persistence/utils' import { executeWebhookJob } from '@/background/webhook-execution' import { resolveEnvVarReferences } from '@/executor/utils/reference-validation' import { isPollingWebhookProvider } from '@/triggers/constants' @@ -672,3 +674,215 @@ export async function queueWebhookExecution( return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) } } + +export interface PolledWebhookEventResult { + success: boolean + error?: string + statusCode?: number +} + +interface PolledWebhookRecord { + id: string + path: string + provider: string | null + blockId: string | null + providerConfig: unknown + credentialSetId: string | null + workflowId: string +} + +interface PolledWorkflowRecord { + id: string + userId: string + workspaceId: string +} + +/** + * Processes a polled webhook event directly, bypassing the HTTP trigger route. + * Used by polling services (Gmail, Outlook, IMAP, RSS) to avoid the self-POST + * anti-pattern where they would otherwise POST back to /api/webhooks/trigger/{path}. + * + * Performs only the steps actually needed for polling providers: + * admission control, preprocessing, block existence check, and queue execution. + */ +export async function processPolledWebhookEvent( + foundWebhook: PolledWebhookRecord, + foundWorkflow: PolledWorkflowRecord, + body: Record | object, + requestId: string +): Promise { + if (!foundWebhook.provider) { + return { success: false, error: 'Webhook has no provider', statusCode: 400 } + } + const provider = foundWebhook.provider + + const ticket = tryAdmit() + if (!ticket) { + logger.warn(`[${requestId}] Admission gate rejected polled webhook event`) + return { success: false, error: 'Server at capacity', statusCode: 429 } + } + + try { + const preprocessResult = await checkWebhookPreprocessing(foundWorkflow, foundWebhook, requestId) + if (preprocessResult.error) { + return { success: false, error: 'Preprocessing failed', statusCode: 500 } + } + + if (foundWebhook.blockId) { + const blockExists = await blockExistsInDeployment(foundWorkflow.id, foundWebhook.blockId) + if (!blockExists) { + logger.info( + `[${requestId}] Trigger block ${foundWebhook.blockId} not found in deployment for workflow ${foundWorkflow.id}` + ) + return { success: false, error: 'Trigger block not found in deployment', statusCode: 404 } + } + } + + const providerConfig = (foundWebhook.providerConfig as Record) || {} + const credentialId = providerConfig.credentialId as string | undefined + const credentialSetId = foundWebhook.credentialSetId as string | undefined + + if (credentialSetId) { + const billingCheck = await verifyCredentialSetBilling(credentialSetId) + if (!billingCheck.valid) { + logger.warn(`[${requestId}] Credential set billing check failed: ${billingCheck.error}`) + return { success: false, error: billingCheck.error, statusCode: 403 } + } + } + + const actorUserId = preprocessResult.actorUserId + if (!actorUserId) { + logger.error(`[${requestId}] No actorUserId provided for webhook ${foundWebhook.id}`) + return { success: false, error: 'Unable to resolve billing account', statusCode: 500 } + } + + const executionId = preprocessResult.executionId ?? generateId() + const correlation = + preprocessResult.correlation ?? + ({ + executionId, + requestId, + source: 'webhook' as const, + workflowId: foundWorkflow.id, + webhookId: foundWebhook.id, + path: foundWebhook.path, + provider, + triggerType: 'webhook', + } satisfies AsyncExecutionCorrelation) + + const payload = { + webhookId: foundWebhook.id, + workflowId: foundWorkflow.id, + userId: actorUserId, + executionId, + requestId, + correlation, + provider, + body, + headers: { 'content-type': 'application/json' } as Record, + path: foundWebhook.path, + blockId: foundWebhook.blockId ?? undefined, + workspaceId: foundWorkflow.workspaceId, + ...(credentialId ? { credentialId } : {}), + } + + if (isPollingWebhookProvider(payload.provider) && !shouldExecuteInline()) { + const jobId = isBullMQEnabled() + ? await enqueueWorkspaceDispatch({ + id: executionId, + workspaceId: foundWorkflow.workspaceId, + lane: 'runtime', + queueName: 'webhook-execution', + bullmqJobName: 'webhook-execution', + bullmqPayload: createBullMQJobData(payload, { + workflowId: foundWorkflow.id, + userId: actorUserId, + correlation, + }), + metadata: { + workflowId: foundWorkflow.id, + userId: actorUserId, + correlation, + }, + }) + : await (await getJobQueue()).enqueue('webhook-execution', payload, { + metadata: { + workflowId: foundWorkflow.id, + workspaceId: foundWorkflow.workspaceId, + userId: actorUserId, + correlation, + }, + }) + logger.info( + `[${requestId}] Queued polling webhook execution task ${jobId} for ${provider} webhook via job queue` + ) + } else { + const jobQueue = await getInlineJobQueue() + const jobId = isBullMQEnabled() + ? await enqueueWorkspaceDispatch({ + id: executionId, + workspaceId: foundWorkflow.workspaceId, + lane: 'runtime', + queueName: 'webhook-execution', + bullmqJobName: 'webhook-execution', + bullmqPayload: createBullMQJobData(payload, { + workflowId: foundWorkflow.id, + userId: actorUserId, + correlation, + }), + metadata: { + workflowId: foundWorkflow.id, + userId: actorUserId, + correlation, + }, + }) + : await jobQueue.enqueue('webhook-execution', payload, { + metadata: { + workflowId: foundWorkflow.id, + workspaceId: foundWorkflow.workspaceId, + userId: actorUserId, + correlation, + }, + }) + logger.info(`[${requestId}] Queued ${provider} webhook execution ${jobId} via inline backend`) + + if (!isBullMQEnabled()) { + void (async () => { + try { + await jobQueue.startJob(jobId) + const output = await executeWebhookJob(payload) + await jobQueue.completeJob(jobId, output) + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error) + logger.error(`[${requestId}] Webhook execution failed`, { + jobId, + error: errorMessage, + }) + try { + await jobQueue.markJobFailed(jobId, errorMessage) + } catch (markFailedError) { + logger.error(`[${requestId}] Failed to mark job as failed`, { + jobId, + error: + markFailedError instanceof Error + ? markFailedError.message + : String(markFailedError), + }) + } + } + })() + } + } + + return { success: true } + } catch (error: unknown) { + if (error instanceof DispatchQueueFullError) { + logger.warn(`[${requestId}] Dispatch queue full for polled webhook: ${error.message}`) + return { success: false, error: 'Service temporarily at capacity', statusCode: 503 } + } + logger.error(`[${requestId}] Failed to process polled webhook event:`, error) + return { success: false, error: 'Internal server error', statusCode: 500 } + } finally { + ticket.release() + } +} diff --git a/apps/sim/lib/webhooks/providers/slack.ts b/apps/sim/lib/webhooks/providers/slack.ts index 1bcedd628b9..d645fd791ef 100644 --- a/apps/sim/lib/webhooks/providers/slack.ts +++ b/apps/sim/lib/webhooks/providers/slack.ts @@ -1,10 +1,13 @@ +import crypto from 'crypto' import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' +import { safeCompare } from '@/lib/core/security/encryption' import { secureFetchWithPinnedIP, validateUrlWithDNS, } from '@/lib/core/security/input-validation.server' import type { + AuthContext, FormatInputContext, FormatInputResult, WebhookProviderHandler, @@ -177,6 +180,44 @@ async function fetchSlackMessageText( } } +/** Maximum allowed timestamp skew (5 minutes) per Slack docs. */ +const SLACK_TIMESTAMP_MAX_SKEW = 300 + +/** + * Validate Slack request signature using HMAC-SHA256. + * Basestring format: `v0:{timestamp}:{rawBody}` + * Signature header format: `v0={hex}` + */ +function validateSlackSignature( + signingSecret: string, + signature: string, + timestamp: string, + rawBody: string +): boolean { + try { + if (!signingSecret || !signature || !rawBody) { + return false + } + + if (!signature.startsWith('v0=')) { + logger.warn('Slack signature has invalid format (missing v0= prefix)') + return false + } + + const providedSignature = signature.substring(3) + const basestring = `v0:${timestamp}:${rawBody}` + const computedHash = crypto + .createHmac('sha256', signingSecret) + .update(basestring, 'utf8') + .digest('hex') + + return safeCompare(computedHash, providedSignature) + } catch (error) { + logger.error('Error validating Slack signature:', error) + return false + } +} + /** * Handle Slack verification challenges */ @@ -190,6 +231,44 @@ export function handleSlackChallenge(body: unknown): NextResponse | null { } export const slackHandler: WebhookProviderHandler = { + verifyAuth({ request, rawBody, requestId, providerConfig }: AuthContext) { + const signingSecret = providerConfig.signingSecret as string | undefined + if (!signingSecret) { + return null + } + + const signature = request.headers.get('x-slack-signature') + const timestamp = request.headers.get('x-slack-request-timestamp') + + if (!signature || !timestamp) { + logger.warn(`[${requestId}] Slack webhook missing signature or timestamp header`) + return new NextResponse('Unauthorized - Missing Slack signature', { status: 401 }) + } + + const now = Math.floor(Date.now() / 1000) + const parsedTimestamp = Number(timestamp) + if (Number.isNaN(parsedTimestamp)) { + logger.warn(`[${requestId}] Slack webhook timestamp is not a valid number`, { timestamp }) + return new NextResponse('Unauthorized - Invalid timestamp', { status: 401 }) + } + const skew = Math.abs(now - parsedTimestamp) + if (skew > SLACK_TIMESTAMP_MAX_SKEW) { + logger.warn(`[${requestId}] Slack webhook timestamp too old`, { + timestamp, + now, + skew, + }) + return new NextResponse('Unauthorized - Request timestamp too old', { status: 401 }) + } + + if (!validateSlackSignature(signingSecret, signature, timestamp, rawBody)) { + logger.warn(`[${requestId}] Slack signature verification failed`) + return new NextResponse('Unauthorized - Invalid Slack signature', { status: 401 }) + } + + return null + }, + handleChallenge(body: unknown) { return handleSlackChallenge(body) }, @@ -262,10 +341,13 @@ export const slackHandler: WebhookProviderHandler = { input: { event: { event_type: eventType, + subtype: (rawEvent?.subtype as string) ?? '', channel, channel_name: '', + channel_type: (rawEvent?.channel_type as string) ?? '', user: (rawEvent?.user as string) || '', user_name: '', + bot_id: (rawEvent?.bot_id as string) ?? '', text, timestamp: messageTs, thread_ts: (rawEvent?.thread_ts as string) || '', diff --git a/apps/sim/lib/webhooks/rss-polling-service.ts b/apps/sim/lib/webhooks/rss-polling-service.ts deleted file mode 100644 index e6727a2f7ca..00000000000 --- a/apps/sim/lib/webhooks/rss-polling-service.ts +++ /dev/null @@ -1,442 +0,0 @@ -import { db } from '@sim/db' -import { webhook, workflow, workflowDeploymentVersion } from '@sim/db/schema' -import { createLogger } from '@sim/logger' -import { and, eq, isNull, or, sql } from 'drizzle-orm' -import Parser from 'rss-parser' -import { pollingIdempotency } from '@/lib/core/idempotency/service' -import { - secureFetchWithPinnedIP, - validateUrlWithDNS, -} from '@/lib/core/security/input-validation.server' -import { getInternalApiBaseUrl } from '@/lib/core/utils/urls' -import { generateShortId } from '@/lib/core/utils/uuid' -import { MAX_CONSECUTIVE_FAILURES } from '@/triggers/constants' - -const logger = createLogger('RssPollingService') -const MAX_GUIDS_TO_TRACK = 100 // Track recent guids to prevent duplicates - -interface RssWebhookConfig { - feedUrl: string - lastCheckedTimestamp?: string - lastSeenGuids?: string[] - etag?: string - lastModified?: string -} - -interface RssItem { - title?: string - link?: string - pubDate?: string - guid?: string - description?: string - content?: string - contentSnippet?: string - author?: string - creator?: string - categories?: string[] - enclosure?: { - url: string - type?: string - length?: string | number - } - isoDate?: string - [key: string]: any -} - -interface RssFeed { - title?: string - link?: string - description?: string - items: RssItem[] -} - -export interface RssWebhookPayload { - title?: string - link?: string - pubDate?: string - item: RssItem - feed: { - title?: string - link?: string - description?: string - } - timestamp: string -} - -const parser = new Parser({ - timeout: 30000, - headers: { - 'User-Agent': 'Sim/1.0 RSS Poller', - }, -}) - -async function markWebhookFailed(webhookId: string) { - try { - const result = await db - .update(webhook) - .set({ - failedCount: sql`COALESCE(${webhook.failedCount}, 0) + 1`, - lastFailedAt: new Date(), - updatedAt: new Date(), - }) - .where(eq(webhook.id, webhookId)) - .returning({ failedCount: webhook.failedCount }) - - const newFailedCount = result[0]?.failedCount || 0 - const shouldDisable = newFailedCount >= MAX_CONSECUTIVE_FAILURES - - if (shouldDisable) { - await db - .update(webhook) - .set({ - isActive: false, - updatedAt: new Date(), - }) - .where(eq(webhook.id, webhookId)) - - logger.warn( - `Webhook ${webhookId} auto-disabled after ${MAX_CONSECUTIVE_FAILURES} consecutive failures` - ) - } - } catch (err) { - logger.error(`Failed to mark webhook ${webhookId} as failed:`, err) - } -} - -async function markWebhookSuccess(webhookId: string) { - try { - await db - .update(webhook) - .set({ - failedCount: 0, - updatedAt: new Date(), - }) - .where(eq(webhook.id, webhookId)) - } catch (err) { - logger.error(`Failed to mark webhook ${webhookId} as successful:`, err) - } -} - -export async function pollRssWebhooks() { - logger.info('Starting RSS webhook polling') - - try { - const activeWebhooksResult = await db - .select({ webhook }) - .from(webhook) - .innerJoin(workflow, eq(webhook.workflowId, workflow.id)) - .leftJoin( - workflowDeploymentVersion, - and( - eq(workflowDeploymentVersion.workflowId, workflow.id), - eq(workflowDeploymentVersion.isActive, true) - ) - ) - .where( - and( - eq(webhook.provider, 'rss'), - eq(webhook.isActive, true), - eq(workflow.isDeployed, true), - or( - eq(webhook.deploymentVersionId, workflowDeploymentVersion.id), - and(isNull(workflowDeploymentVersion.id), isNull(webhook.deploymentVersionId)) - ) - ) - ) - - const activeWebhooks = activeWebhooksResult.map((r) => r.webhook) - - if (!activeWebhooks.length) { - logger.info('No active RSS webhooks found') - return { total: 0, successful: 0, failed: 0, details: [] } - } - - logger.info(`Found ${activeWebhooks.length} active RSS webhooks`) - - const CONCURRENCY = 10 - const running: Promise[] = [] - let successCount = 0 - let failureCount = 0 - - const enqueue = async (webhookData: (typeof activeWebhooks)[number]) => { - const webhookId = webhookData.id - const requestId = generateShortId() - - try { - const config = webhookData.providerConfig as unknown as RssWebhookConfig - - if (!config?.feedUrl) { - logger.error(`[${requestId}] Missing feedUrl for webhook ${webhookId}`) - await markWebhookFailed(webhookId) - failureCount++ - return - } - - const now = new Date() - - const { feed, items: newItems } = await fetchNewRssItems(config, requestId) - - if (!newItems.length) { - await updateWebhookConfig(webhookId, now.toISOString(), []) - await markWebhookSuccess(webhookId) - logger.info(`[${requestId}] No new items found for webhook ${webhookId}`) - successCount++ - return - } - - logger.info(`[${requestId}] Found ${newItems.length} new items for webhook ${webhookId}`) - - const { processedCount, failedCount: itemFailedCount } = await processRssItems( - newItems, - feed, - webhookData, - requestId - ) - - const newGuids = newItems - .map((item) => item.guid || item.link || '') - .filter((guid) => guid.length > 0) - - await updateWebhookConfig(webhookId, now.toISOString(), newGuids) - - if (itemFailedCount > 0 && processedCount === 0) { - await markWebhookFailed(webhookId) - failureCount++ - logger.warn( - `[${requestId}] All ${itemFailedCount} items failed to process for webhook ${webhookId}` - ) - } else { - await markWebhookSuccess(webhookId) - successCount++ - logger.info( - `[${requestId}] Successfully processed ${processedCount} items for webhook ${webhookId}${itemFailedCount > 0 ? ` (${itemFailedCount} failed)` : ''}` - ) - } - } catch (error) { - logger.error(`[${requestId}] Error processing RSS webhook ${webhookId}:`, error) - await markWebhookFailed(webhookId) - failureCount++ - } - } - - for (const webhookData of activeWebhooks) { - const promise = enqueue(webhookData) - .then(() => {}) - .catch((err) => { - logger.error('Unexpected error in webhook processing:', err) - failureCount++ - }) - - running.push(promise) - - if (running.length >= CONCURRENCY) { - const completedIdx = await Promise.race(running.map((p, i) => p.then(() => i))) - running.splice(completedIdx, 1) - } - } - - await Promise.allSettled(running) - - const summary = { - total: activeWebhooks.length, - successful: successCount, - failed: failureCount, - details: [], - } - - logger.info('RSS polling completed', { - total: summary.total, - successful: summary.successful, - failed: summary.failed, - }) - - return summary - } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error' - logger.error('Error in RSS polling service:', errorMessage) - throw error - } -} - -async function fetchNewRssItems( - config: RssWebhookConfig, - requestId: string -): Promise<{ feed: RssFeed; items: RssItem[] }> { - try { - const urlValidation = await validateUrlWithDNS(config.feedUrl, 'feedUrl') - if (!urlValidation.isValid) { - logger.error(`[${requestId}] Invalid RSS feed URL: ${urlValidation.error}`) - throw new Error(`Invalid RSS feed URL: ${urlValidation.error}`) - } - - const response = await secureFetchWithPinnedIP(config.feedUrl, urlValidation.resolvedIP!, { - headers: { - 'User-Agent': 'Sim/1.0 RSS Poller', - Accept: 'application/rss+xml, application/xml, text/xml, */*', - }, - timeout: 30000, - }) - - if (!response.ok) { - await response.text().catch(() => {}) - throw new Error(`Failed to fetch RSS feed: ${response.status} ${response.statusText}`) - } - - const xmlContent = await response.text() - - const feed = await parser.parseString(xmlContent) - - if (!feed.items || !feed.items.length) { - return { feed: feed as RssFeed, items: [] } - } - - const lastCheckedTime = config.lastCheckedTimestamp - ? new Date(config.lastCheckedTimestamp) - : null - const lastSeenGuids = new Set(config.lastSeenGuids || []) - - const newItems = feed.items.filter((item) => { - const itemGuid = item.guid || item.link || '' - - if (itemGuid && lastSeenGuids.has(itemGuid)) { - return false - } - - if (lastCheckedTime && item.isoDate) { - const itemDate = new Date(item.isoDate) - if (itemDate <= lastCheckedTime) { - return false - } - } - - return true - }) - - newItems.sort((a, b) => { - const dateA = a.isoDate ? new Date(a.isoDate).getTime() : 0 - const dateB = b.isoDate ? new Date(b.isoDate).getTime() : 0 - return dateB - dateA - }) - - const limitedItems = newItems.slice(0, 25) - - logger.info( - `[${requestId}] Found ${newItems.length} new items (processing ${limitedItems.length})` - ) - - return { feed: feed as RssFeed, items: limitedItems as RssItem[] } - } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error' - logger.error(`[${requestId}] Error fetching RSS feed:`, errorMessage) - throw error - } -} - -async function processRssItems( - items: RssItem[], - feed: RssFeed, - webhookData: any, - requestId: string -): Promise<{ processedCount: number; failedCount: number }> { - let processedCount = 0 - let failedCount = 0 - - for (const item of items) { - try { - const itemGuid = item.guid || item.link || `${item.title}-${item.pubDate}` - - await pollingIdempotency.executeWithIdempotency( - 'rss', - `${webhookData.id}:${itemGuid}`, - async () => { - const payload: RssWebhookPayload = { - title: item.title, - link: item.link, - pubDate: item.pubDate, - item: { - title: item.title, - link: item.link, - pubDate: item.pubDate, - guid: item.guid, - description: item.description, - content: item.content, - contentSnippet: item.contentSnippet, - author: item.author || item.creator, - categories: item.categories, - enclosure: item.enclosure, - isoDate: item.isoDate, - }, - feed: { - title: feed.title, - link: feed.link, - description: feed.description, - }, - timestamp: new Date().toISOString(), - } - - const webhookUrl = `${getInternalApiBaseUrl()}/api/webhooks/trigger/${webhookData.path}` - - const response = await fetch(webhookUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'User-Agent': 'Sim/1.0', - }, - body: JSON.stringify(payload), - }) - - if (!response.ok) { - const errorText = await response.text() - logger.error( - `[${requestId}] Failed to trigger webhook for item ${itemGuid}:`, - response.status, - errorText - ) - throw new Error(`Webhook request failed: ${response.status} - ${errorText}`) - } - - return { - itemGuid, - webhookStatus: response.status, - processed: true, - } - } - ) - - logger.info( - `[${requestId}] Successfully processed item ${item.title || itemGuid} for webhook ${webhookData.id}` - ) - processedCount++ - } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error' - logger.error(`[${requestId}] Error processing item:`, errorMessage) - failedCount++ - } - } - - return { processedCount, failedCount } -} - -async function updateWebhookConfig(webhookId: string, timestamp: string, newGuids: string[]) { - try { - const result = await db.select().from(webhook).where(eq(webhook.id, webhookId)) - const existingConfig = (result[0]?.providerConfig as Record) || {} - - const existingGuids = existingConfig.lastSeenGuids || [] - const allGuids = [...newGuids, ...existingGuids].slice(0, MAX_GUIDS_TO_TRACK) - - await db - .update(webhook) - .set({ - providerConfig: { - ...existingConfig, - lastCheckedTimestamp: timestamp, - lastSeenGuids: allGuids, - } as any, - updatedAt: new Date(), - }) - .where(eq(webhook.id, webhookId)) - } catch (err) { - logger.error(`Failed to update webhook ${webhookId} config:`, err) - } -} diff --git a/apps/sim/lib/workflows/lifecycle.ts b/apps/sim/lib/workflows/lifecycle.ts index 8867816da21..c9ff0a9bcfa 100644 --- a/apps/sim/lib/workflows/lifecycle.ts +++ b/apps/sim/lib/workflows/lifecycle.ts @@ -1,6 +1,7 @@ import { db } from '@sim/db' import { a2aAgent, + apiKey, chat, form, webhook, @@ -9,12 +10,14 @@ import { workflowFolder, workflowMcpTool, workflowSchedule, + workspace, } from '@sim/db/schema' import { createLogger } from '@sim/logger' import { and, eq, inArray, isNull } from 'drizzle-orm' import { env } from '@/lib/core/config/env' import { getRedisClient } from '@/lib/core/config/redis' import { PlatformEvents } from '@/lib/core/telemetry' +import { generateRequestId } from '@/lib/core/utils/request' import { mcpPubSub } from '@/lib/mcp/pubsub' import { getWorkflowById } from '@/lib/workflows/utils' @@ -379,3 +382,29 @@ export async function archiveWorkflowsByIdsInWorkspace( options ) } + +/** + * Disables all resources owned by a banned user by archiving every workspace + * they own (cascading to workflows, chats, forms, KBs, tables, files, etc.) + * and deleting their personal API keys. + */ +export async function disableUserResources(userId: string): Promise { + const requestId = generateRequestId() + logger.info(`[${requestId}] Disabling resources for banned user ${userId}`) + + const { archiveWorkspace } = await import('@/lib/workspaces/lifecycle') + + const ownedWorkspaces = await db + .select({ id: workspace.id }) + .from(workspace) + .where(and(eq(workspace.ownerId, userId), isNull(workspace.archivedAt))) + + await Promise.all([ + ...ownedWorkspaces.map((w) => archiveWorkspace(w.id, { requestId })), + db.delete(apiKey).where(eq(apiKey.userId, userId)), + ]) + + logger.info( + `[${requestId}] Disabled resources for user ${userId}: archived ${ownedWorkspaces.length} workspaces, deleted API keys` + ) +} diff --git a/apps/sim/lib/workflows/triggers/trigger-utils.ts b/apps/sim/lib/workflows/triggers/trigger-utils.ts index f1ee5ce7450..699cbc51756 100644 --- a/apps/sim/lib/workflows/triggers/trigger-utils.ts +++ b/apps/sim/lib/workflows/triggers/trigger-utils.ts @@ -67,7 +67,9 @@ function generateMockValue(type: string, _description?: string, fieldName?: stri } /** - * Recursively processes nested output structures + * Recursively processes nested output structures, expanding JSON-Schema-style + * objects/arrays that define `properties` or `items` instead of returning + * a generic placeholder. */ function processOutputField(key: string, field: unknown, depth = 0, maxDepth = 10): unknown { if (depth > maxDepth) { @@ -80,7 +82,30 @@ function processOutputField(key: string, field: unknown, depth = 0, maxDepth = 1 'type' in field && typeof (field as Record).type === 'string' ) { - const typedField = field as { type: string; description?: string } + const typedField = field as { + type: string + description?: string + properties?: Record + items?: unknown + } + + if ( + (typedField.type === 'object' || typedField.type === 'json') && + typedField.properties && + typeof typedField.properties === 'object' + ) { + const nestedObject: Record = {} + for (const [nestedKey, nestedField] of Object.entries(typedField.properties)) { + nestedObject[nestedKey] = processOutputField(nestedKey, nestedField, depth + 1, maxDepth) + } + return nestedObject + } + + if (typedField.type === 'array' && typedField.items && typeof typedField.items === 'object') { + const itemValue = processOutputField(`${key}_item`, typedField.items, depth + 1, maxDepth) + return [itemValue] + } + return generateMockValue(typedField.type, typedField.description, key) } diff --git a/apps/sim/lib/workspaces/colors.ts b/apps/sim/lib/workspaces/colors.ts index 3a0b80af93a..b652ebd36d9 100644 --- a/apps/sim/lib/workspaces/colors.ts +++ b/apps/sim/lib/workspaces/colors.ts @@ -77,6 +77,14 @@ function withAlpha(hexColor: string, alpha: number): string { return `rgba(${r}, ${g}, ${b}, ${Math.min(Math.max(alpha, 0), 1)})` } +/** + * Returns the hex color with 60/ff (~38%) alpha — used for workflow color border accents. + * Returns `undefined` when `color` is undefined so callers can pass it directly to `borderColor`. + */ +export function workflowBorderColor(color: string | undefined): string | undefined { + return color ? `${color}60` : undefined +} + function buildGradient(fromColor: string, toColor: string, rotationSeed: number): string { const rotation = (rotationSeed * 25) % 360 return `linear-gradient(${rotation}deg, ${fromColor}, ${toColor})` diff --git a/apps/sim/tools/athena/create_named_query.ts b/apps/sim/tools/athena/create_named_query.ts new file mode 100644 index 00000000000..8aa9c2aad0b --- /dev/null +++ b/apps/sim/tools/athena/create_named_query.ts @@ -0,0 +1,102 @@ +import type { + AthenaCreateNamedQueryParams, + AthenaCreateNamedQueryResponse, +} from '@/tools/athena/types' +import type { ToolConfig } from '@/tools/types' + +export const createNamedQueryTool: ToolConfig< + AthenaCreateNamedQueryParams, + AthenaCreateNamedQueryResponse +> = { + id: 'athena_create_named_query', + name: 'Athena Create Named Query', + description: 'Create a saved/named query in AWS Athena', + 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', + }, + name: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Name for the saved query', + }, + database: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Database the query runs against', + }, + queryString: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'SQL query string to save', + }, + description: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Description of the named query', + }, + workGroup: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Workgroup to create the named query in', + }, + }, + + request: { + url: '/api/tools/athena/create-named-query', + method: 'POST', + headers: () => ({ 'Content-Type': 'application/json' }), + body: (params) => ({ + region: params.awsRegion, + accessKeyId: params.awsAccessKeyId, + secretAccessKey: params.awsSecretAccessKey, + name: params.name, + database: params.database, + queryString: params.queryString, + ...(params.description && { description: params.description }), + ...(params.workGroup && { workGroup: params.workGroup }), + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.error || 'Failed to create Athena named query') + } + return { + success: true, + output: { + namedQueryId: data.output.namedQueryId, + }, + } + }, + + outputs: { + namedQueryId: { + type: 'string', + description: 'ID of the created named query', + }, + }, +} diff --git a/apps/sim/tools/athena/get_named_query.ts b/apps/sim/tools/athena/get_named_query.ts new file mode 100644 index 00000000000..28cafac4579 --- /dev/null +++ b/apps/sim/tools/athena/get_named_query.ts @@ -0,0 +1,76 @@ +import type { AthenaGetNamedQueryParams, AthenaGetNamedQueryResponse } from '@/tools/athena/types' +import type { ToolConfig } from '@/tools/types' + +export const getNamedQueryTool: ToolConfig = + { + id: 'athena_get_named_query', + name: 'Athena Get Named Query', + description: 'Get details of a saved/named query in AWS Athena', + 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', + }, + namedQueryId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Named query ID to retrieve', + }, + }, + + request: { + url: '/api/tools/athena/get-named-query', + method: 'POST', + headers: () => ({ 'Content-Type': 'application/json' }), + body: (params) => ({ + region: params.awsRegion, + accessKeyId: params.awsAccessKeyId, + secretAccessKey: params.awsSecretAccessKey, + namedQueryId: params.namedQueryId, + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.error || 'Failed to get Athena named query') + } + return { + success: true, + output: { + namedQueryId: data.output.namedQueryId, + name: data.output.name, + description: data.output.description ?? null, + database: data.output.database, + queryString: data.output.queryString, + workGroup: data.output.workGroup ?? null, + }, + } + }, + + outputs: { + namedQueryId: { type: 'string', description: 'Named query ID' }, + name: { type: 'string', description: 'Name of the saved query' }, + description: { type: 'string', description: 'Query description', optional: true }, + database: { type: 'string', description: 'Database the query runs against' }, + queryString: { type: 'string', description: 'SQL query string' }, + workGroup: { type: 'string', description: 'Workgroup name', optional: true }, + }, + } diff --git a/apps/sim/tools/athena/get_query_execution.ts b/apps/sim/tools/athena/get_query_execution.ts new file mode 100644 index 00000000000..8ebe407813a --- /dev/null +++ b/apps/sim/tools/athena/get_query_execution.ts @@ -0,0 +1,144 @@ +import type { + AthenaGetQueryExecutionParams, + AthenaGetQueryExecutionResponse, +} from '@/tools/athena/types' +import type { ToolConfig } from '@/tools/types' + +export const getQueryExecutionTool: ToolConfig< + AthenaGetQueryExecutionParams, + AthenaGetQueryExecutionResponse +> = { + id: 'athena_get_query_execution', + name: 'Athena Get Query Execution', + description: 'Get the status and details of an Athena query execution', + 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', + }, + queryExecutionId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Query execution ID to check', + }, + }, + + request: { + url: '/api/tools/athena/get-query-execution', + method: 'POST', + headers: () => ({ 'Content-Type': 'application/json' }), + body: (params) => ({ + region: params.awsRegion, + accessKeyId: params.awsAccessKeyId, + secretAccessKey: params.awsSecretAccessKey, + queryExecutionId: params.queryExecutionId, + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.error || 'Failed to get Athena query execution') + } + return { + success: true, + output: { + queryExecutionId: data.output.queryExecutionId, + query: data.output.query, + state: data.output.state, + stateChangeReason: data.output.stateChangeReason ?? null, + statementType: data.output.statementType ?? null, + database: data.output.database ?? null, + catalog: data.output.catalog ?? null, + workGroup: data.output.workGroup ?? null, + submissionDateTime: data.output.submissionDateTime ?? null, + completionDateTime: data.output.completionDateTime ?? null, + dataScannedInBytes: data.output.dataScannedInBytes ?? null, + engineExecutionTimeInMillis: data.output.engineExecutionTimeInMillis ?? null, + queryPlanningTimeInMillis: data.output.queryPlanningTimeInMillis ?? null, + queryQueueTimeInMillis: data.output.queryQueueTimeInMillis ?? null, + totalExecutionTimeInMillis: data.output.totalExecutionTimeInMillis ?? null, + outputLocation: data.output.outputLocation ?? null, + }, + } + }, + + outputs: { + queryExecutionId: { type: 'string', description: 'Query execution ID' }, + query: { type: 'string', description: 'SQL query string' }, + state: { + type: 'string', + description: 'Query state (QUEUED, RUNNING, SUCCEEDED, FAILED, CANCELLED)', + }, + stateChangeReason: { + type: 'string', + description: 'Reason for state change (e.g., error message)', + optional: true, + }, + statementType: { + type: 'string', + description: 'Statement type (DDL, DML, UTILITY)', + optional: true, + }, + database: { type: 'string', description: 'Database name', optional: true }, + catalog: { type: 'string', description: 'Data catalog name', optional: true }, + workGroup: { type: 'string', description: 'Workgroup name', optional: true }, + submissionDateTime: { + type: 'number', + description: 'Query submission time (Unix epoch ms)', + optional: true, + }, + completionDateTime: { + type: 'number', + description: 'Query completion time (Unix epoch ms)', + optional: true, + }, + dataScannedInBytes: { + type: 'number', + description: 'Amount of data scanned in bytes', + optional: true, + }, + engineExecutionTimeInMillis: { + type: 'number', + description: 'Engine execution time in milliseconds', + optional: true, + }, + queryPlanningTimeInMillis: { + type: 'number', + description: 'Query planning time in milliseconds', + optional: true, + }, + queryQueueTimeInMillis: { + type: 'number', + description: 'Time the query spent in queue in milliseconds', + optional: true, + }, + totalExecutionTimeInMillis: { + type: 'number', + description: 'Total execution time in milliseconds', + optional: true, + }, + outputLocation: { + type: 'string', + description: 'S3 location of query results', + optional: true, + }, + }, +} diff --git a/apps/sim/tools/athena/get_query_results.ts b/apps/sim/tools/athena/get_query_results.ts new file mode 100644 index 00000000000..8e53af03530 --- /dev/null +++ b/apps/sim/tools/athena/get_query_results.ts @@ -0,0 +1,105 @@ +import type { + AthenaGetQueryResultsParams, + AthenaGetQueryResultsResponse, +} from '@/tools/athena/types' +import type { ToolConfig } from '@/tools/types' + +export const getQueryResultsTool: ToolConfig< + AthenaGetQueryResultsParams, + AthenaGetQueryResultsResponse +> = { + id: 'athena_get_query_results', + name: 'Athena Get Query Results', + description: 'Retrieve the results of a completed Athena query execution', + 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', + }, + queryExecutionId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Query execution ID to get results for', + }, + maxResults: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Maximum number of rows to return (1-999)', + }, + nextToken: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Pagination token from a previous request', + }, + }, + + request: { + url: '/api/tools/athena/get-query-results', + method: 'POST', + headers: () => ({ 'Content-Type': 'application/json' }), + body: (params) => ({ + region: params.awsRegion, + accessKeyId: params.awsAccessKeyId, + secretAccessKey: params.awsSecretAccessKey, + queryExecutionId: params.queryExecutionId, + ...(params.maxResults !== undefined && { maxResults: params.maxResults }), + ...(params.nextToken && { nextToken: params.nextToken }), + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.error || 'Failed to get Athena query results') + } + return { + success: true, + output: { + columns: data.output.columns ?? [], + rows: data.output.rows ?? [], + nextToken: data.output.nextToken ?? null, + updateCount: data.output.updateCount ?? null, + }, + } + }, + + outputs: { + columns: { + type: 'array', + description: 'Column metadata (name and type)', + }, + rows: { + type: 'array', + description: 'Result rows as key-value objects', + }, + nextToken: { + type: 'string', + description: 'Pagination token for next page of results', + optional: true, + }, + updateCount: { + type: 'number', + description: 'Number of rows affected (for INSERT/UPDATE statements)', + optional: true, + }, + }, +} diff --git a/apps/sim/tools/athena/index.ts b/apps/sim/tools/athena/index.ts new file mode 100644 index 00000000000..5461e01117f --- /dev/null +++ b/apps/sim/tools/athena/index.ts @@ -0,0 +1,19 @@ +import { createNamedQueryTool } from '@/tools/athena/create_named_query' +import { getNamedQueryTool } from '@/tools/athena/get_named_query' +import { getQueryExecutionTool } from '@/tools/athena/get_query_execution' +import { getQueryResultsTool } from '@/tools/athena/get_query_results' +import { listNamedQueriesTool } from '@/tools/athena/list_named_queries' +import { listQueryExecutionsTool } from '@/tools/athena/list_query_executions' +import { startQueryTool } from '@/tools/athena/start_query' +import { stopQueryTool } from '@/tools/athena/stop_query' + +export const athenaCreateNamedQueryTool = createNamedQueryTool +export const athenaGetNamedQueryTool = getNamedQueryTool +export const athenaGetQueryExecutionTool = getQueryExecutionTool +export const athenaGetQueryResultsTool = getQueryResultsTool +export const athenaListNamedQueriesTool = listNamedQueriesTool +export const athenaListQueryExecutionsTool = listQueryExecutionsTool +export const athenaStartQueryTool = startQueryTool +export const athenaStopQueryTool = stopQueryTool + +export * from '@/tools/athena/types' diff --git a/apps/sim/tools/athena/list_named_queries.ts b/apps/sim/tools/athena/list_named_queries.ts new file mode 100644 index 00000000000..33ce7d48615 --- /dev/null +++ b/apps/sim/tools/athena/list_named_queries.ts @@ -0,0 +1,94 @@ +import type { + AthenaListNamedQueriesParams, + AthenaListNamedQueriesResponse, +} from '@/tools/athena/types' +import type { ToolConfig } from '@/tools/types' + +export const listNamedQueriesTool: ToolConfig< + AthenaListNamedQueriesParams, + AthenaListNamedQueriesResponse +> = { + id: 'athena_list_named_queries', + name: 'Athena List Named Queries', + description: 'List saved/named query IDs in AWS Athena', + 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', + }, + workGroup: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Workgroup to list named queries for', + }, + maxResults: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Maximum number of results (0-50)', + }, + nextToken: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Pagination token from a previous request', + }, + }, + + request: { + url: '/api/tools/athena/list-named-queries', + method: 'POST', + headers: () => ({ 'Content-Type': 'application/json' }), + body: (params) => ({ + region: params.awsRegion, + accessKeyId: params.awsAccessKeyId, + secretAccessKey: params.awsSecretAccessKey, + ...(params.workGroup && { workGroup: params.workGroup }), + ...(params.maxResults !== undefined && { maxResults: params.maxResults }), + ...(params.nextToken && { nextToken: params.nextToken }), + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.error || 'Failed to list Athena named queries') + } + return { + success: true, + output: { + namedQueryIds: data.output.namedQueryIds ?? [], + nextToken: data.output.nextToken ?? null, + }, + } + }, + + outputs: { + namedQueryIds: { + type: 'array', + description: 'List of named query IDs', + }, + nextToken: { + type: 'string', + description: 'Pagination token for next page', + optional: true, + }, + }, +} diff --git a/apps/sim/tools/athena/list_query_executions.ts b/apps/sim/tools/athena/list_query_executions.ts new file mode 100644 index 00000000000..89c03d997f7 --- /dev/null +++ b/apps/sim/tools/athena/list_query_executions.ts @@ -0,0 +1,94 @@ +import type { + AthenaListQueryExecutionsParams, + AthenaListQueryExecutionsResponse, +} from '@/tools/athena/types' +import type { ToolConfig } from '@/tools/types' + +export const listQueryExecutionsTool: ToolConfig< + AthenaListQueryExecutionsParams, + AthenaListQueryExecutionsResponse +> = { + id: 'athena_list_query_executions', + name: 'Athena List Query Executions', + description: 'List recent Athena query execution IDs', + 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', + }, + workGroup: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Workgroup to list executions for (default: primary)', + }, + maxResults: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Maximum number of results (0-50)', + }, + nextToken: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Pagination token from a previous request', + }, + }, + + request: { + url: '/api/tools/athena/list-query-executions', + method: 'POST', + headers: () => ({ 'Content-Type': 'application/json' }), + body: (params) => ({ + region: params.awsRegion, + accessKeyId: params.awsAccessKeyId, + secretAccessKey: params.awsSecretAccessKey, + ...(params.workGroup && { workGroup: params.workGroup }), + ...(params.maxResults !== undefined && { maxResults: params.maxResults }), + ...(params.nextToken && { nextToken: params.nextToken }), + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.error || 'Failed to list Athena query executions') + } + return { + success: true, + output: { + queryExecutionIds: data.output.queryExecutionIds ?? [], + nextToken: data.output.nextToken ?? null, + }, + } + }, + + outputs: { + queryExecutionIds: { + type: 'array', + description: 'List of query execution IDs', + }, + nextToken: { + type: 'string', + description: 'Pagination token for next page', + optional: true, + }, + }, +} diff --git a/apps/sim/tools/athena/start_query.ts b/apps/sim/tools/athena/start_query.ts new file mode 100644 index 00000000000..efaf0554a6e --- /dev/null +++ b/apps/sim/tools/athena/start_query.ts @@ -0,0 +1,96 @@ +import type { AthenaStartQueryParams, AthenaStartQueryResponse } from '@/tools/athena/types' +import type { ToolConfig } from '@/tools/types' + +export const startQueryTool: ToolConfig = { + id: 'athena_start_query', + name: 'Athena Start Query', + description: 'Start an SQL query execution in AWS Athena', + 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', + }, + queryString: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'SQL query string to execute', + }, + database: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Database name within the catalog', + }, + catalog: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Data catalog name (default: AwsDataCatalog)', + }, + outputLocation: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'S3 output location for query results (e.g., s3://bucket/path/)', + }, + workGroup: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Workgroup to execute the query in (default: primary)', + }, + }, + + request: { + url: '/api/tools/athena/start-query', + method: 'POST', + headers: () => ({ 'Content-Type': 'application/json' }), + body: (params) => ({ + region: params.awsRegion, + accessKeyId: params.awsAccessKeyId, + secretAccessKey: params.awsSecretAccessKey, + queryString: params.queryString, + ...(params.database && { database: params.database }), + ...(params.catalog && { catalog: params.catalog }), + ...(params.outputLocation && { outputLocation: params.outputLocation }), + ...(params.workGroup && { workGroup: params.workGroup }), + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.error || 'Failed to start Athena query') + } + return { + success: true, + output: { + queryExecutionId: data.output.queryExecutionId, + }, + } + }, + + outputs: { + queryExecutionId: { + type: 'string', + description: 'Unique ID of the started query execution', + }, + }, +} diff --git a/apps/sim/tools/athena/stop_query.ts b/apps/sim/tools/athena/stop_query.ts new file mode 100644 index 00000000000..94f88eb61e0 --- /dev/null +++ b/apps/sim/tools/athena/stop_query.ts @@ -0,0 +1,68 @@ +import type { AthenaStopQueryParams, AthenaStopQueryResponse } from '@/tools/athena/types' +import type { ToolConfig } from '@/tools/types' + +export const stopQueryTool: ToolConfig = { + id: 'athena_stop_query', + name: 'Athena Stop Query', + description: 'Stop a running Athena query execution', + 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', + }, + queryExecutionId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Query execution ID to stop', + }, + }, + + request: { + url: '/api/tools/athena/stop-query', + method: 'POST', + headers: () => ({ 'Content-Type': 'application/json' }), + body: (params) => ({ + region: params.awsRegion, + accessKeyId: params.awsAccessKeyId, + secretAccessKey: params.awsSecretAccessKey, + queryExecutionId: params.queryExecutionId, + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.error || 'Failed to stop Athena query') + } + return { + success: true, + output: { + success: true, + }, + } + }, + + outputs: { + success: { + type: 'boolean', + description: 'Whether the query was successfully stopped', + }, + }, +} diff --git a/apps/sim/tools/athena/types.ts b/apps/sim/tools/athena/types.ts new file mode 100644 index 00000000000..4b7a1a6d319 --- /dev/null +++ b/apps/sim/tools/athena/types.ts @@ -0,0 +1,126 @@ +import type { ToolResponse } from '@/tools/types' + +export interface AthenaConnectionConfig { + awsRegion: string + awsAccessKeyId: string + awsSecretAccessKey: string +} + +export interface AthenaStartQueryParams extends AthenaConnectionConfig { + queryString: string + database?: string + catalog?: string + outputLocation?: string + workGroup?: string +} + +export interface AthenaStartQueryResponse extends ToolResponse { + output: { + queryExecutionId: string + } +} + +export interface AthenaGetQueryExecutionParams extends AthenaConnectionConfig { + queryExecutionId: string +} + +export interface AthenaGetQueryExecutionResponse extends ToolResponse { + output: { + queryExecutionId: string + query: string + state: string + stateChangeReason: string | null + statementType: string | null + database: string | null + catalog: string | null + workGroup: string | null + submissionDateTime: number | null + completionDateTime: number | null + dataScannedInBytes: number | null + engineExecutionTimeInMillis: number | null + queryPlanningTimeInMillis: number | null + queryQueueTimeInMillis: number | null + totalExecutionTimeInMillis: number | null + outputLocation: string | null + } +} + +export interface AthenaGetQueryResultsParams extends AthenaConnectionConfig { + queryExecutionId: string + maxResults?: number + nextToken?: string +} + +export interface AthenaGetQueryResultsResponse extends ToolResponse { + output: { + columns: { name: string; type: string }[] + rows: Record[] + nextToken: string | null + updateCount: number | null + } +} + +export interface AthenaStopQueryParams extends AthenaConnectionConfig { + queryExecutionId: string +} + +export interface AthenaStopQueryResponse extends ToolResponse { + output: { + success: boolean + } +} + +export interface AthenaListQueryExecutionsParams extends AthenaConnectionConfig { + workGroup?: string + maxResults?: number + nextToken?: string +} + +export interface AthenaListQueryExecutionsResponse extends ToolResponse { + output: { + queryExecutionIds: string[] + nextToken: string | null + } +} + +export interface AthenaCreateNamedQueryParams extends AthenaConnectionConfig { + name: string + database: string + queryString: string + description?: string + workGroup?: string +} + +export interface AthenaCreateNamedQueryResponse extends ToolResponse { + output: { + namedQueryId: string + } +} + +export interface AthenaGetNamedQueryParams extends AthenaConnectionConfig { + namedQueryId: string +} + +export interface AthenaGetNamedQueryResponse extends ToolResponse { + output: { + namedQueryId: string + name: string + description: string | null + database: string + queryString: string + workGroup: string | null + } +} + +export interface AthenaListNamedQueriesParams extends AthenaConnectionConfig { + workGroup?: string + maxResults?: number + nextToken?: string +} + +export interface AthenaListNamedQueriesResponse extends ToolResponse { + output: { + namedQueryIds: string[] + nextToken: string | null + } +} diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index 85d08da46c5..f094eddcdf9 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -149,6 +149,16 @@ import { ashbySearchCandidatesTool, ashbyUpdateCandidateTool, } from '@/tools/ashby' +import { + athenaCreateNamedQueryTool, + athenaGetNamedQueryTool, + athenaGetQueryExecutionTool, + athenaGetQueryResultsTool, + athenaListNamedQueriesTool, + athenaListQueryExecutionsTool, + athenaStartQueryTool, + athenaStopQueryTool, +} from '@/tools/athena' import { attioAssertRecordTool, attioCreateCommentTool, @@ -2830,6 +2840,14 @@ export const tools: Record = { ashby_remove_candidate_tag: ashbyRemoveCandidateTagTool, ashby_search_candidates: ashbySearchCandidatesTool, ashby_update_candidate: ashbyUpdateCandidateTool, + athena_create_named_query: athenaCreateNamedQueryTool, + athena_get_named_query: athenaGetNamedQueryTool, + athena_get_query_execution: athenaGetQueryExecutionTool, + athena_get_query_results: athenaGetQueryResultsTool, + athena_list_named_queries: athenaListNamedQueriesTool, + athena_list_query_executions: athenaListQueryExecutionsTool, + athena_start_query: athenaStartQueryTool, + athena_stop_query: athenaStopQueryTool, brandfetch_get_brand: brandfetchGetBrandTool, brandfetch_search: brandfetchSearchTool, box_copy_file: boxCopyFileTool, diff --git a/apps/sim/triggers/slack/webhook.ts b/apps/sim/triggers/slack/webhook.ts index 3f1bbe2c0f7..2fa8966ae63 100644 --- a/apps/sim/triggers/slack/webhook.ts +++ b/apps/sim/triggers/slack/webhook.ts @@ -68,7 +68,7 @@ export const slackWebhookTrigger: TriggerConfig = { 'If you don\'t have an app:
  • Create an app from scratch
  • Give it a name and select your workspace
', 'Go to "Basic Information", find the "Signing Secret", and paste it in the field above.', 'Go to "OAuth & Permissions" and add bot token scopes:
  • app_mentions:read - For viewing messages that tag your bot with an @
  • chat:write - To send messages to channels your bot is a part of
  • files:read - To access files and images shared in messages
  • reactions:read - For listening to emoji reactions and fetching reacted-to message text
', - 'Go to "Event Subscriptions":
  • Enable events
  • Under "Subscribe to Bot Events", add app_mention to listen to messages that mention your bot
  • For reaction events, also add reaction_added and/or reaction_removed
  • Paste the Webhook URL above into the "Request URL" field
', + 'Go to "Event Subscriptions":
  • Enable events
  • Under "Subscribe to Bot Events", add app_mention to listen to messages that mention your bot
  • To receive all channel messages, add message.channels. For DMs add message.im, for group DMs add message.mpim, for private channels add message.groups
  • For reaction events, also add reaction_added and/or reaction_removed
  • Paste the Webhook URL above into the "Request URL" field
', 'Go to "Install App" in the left sidebar and install the app into your desired Slack workspace and channel.', 'Copy the "Bot User OAuth Token" (starts with xoxb-) and paste it in the Bot Token field above to enable file downloads.', 'Save changes in both Slack and here.', @@ -92,6 +92,11 @@ export const slackWebhookTrigger: TriggerConfig = { type: 'string', description: 'Type of Slack event (e.g., app_mention, message)', }, + subtype: { + type: 'string', + description: + 'Message subtype (e.g., channel_join, channel_leave, bot_message, file_share). Null for regular user messages', + }, channel: { type: 'string', description: 'Slack channel ID where the event occurred', @@ -100,6 +105,11 @@ export const slackWebhookTrigger: TriggerConfig = { type: 'string', description: 'Human-readable channel name', }, + channel_type: { + type: 'string', + description: + 'Type of channel (e.g., channel, group, im, mpim). Useful for distinguishing DMs from public channels', + }, user: { type: 'string', description: 'User ID who triggered the event', @@ -108,6 +118,10 @@ export const slackWebhookTrigger: TriggerConfig = { type: 'string', description: 'Username who triggered the event', }, + bot_id: { + type: 'string', + description: 'Bot ID if the message was sent by a bot. Null for human users', + }, text: { type: 'string', description: 'Message text content', diff --git a/bun.lock b/bun.lock index 44ba1a838c9..e05bc532f5e 100644 --- a/bun.lock +++ b/bun.lock @@ -4,6 +4,9 @@ "workspaces": { "": { "name": "simstudio", + "dependencies": { + "@aws-sdk/client-athena": "3.1024.0", + }, "devDependencies": { "@biomejs/biome": "2.0.0-beta.5", "@octokit/rest": "^21.0.0", @@ -411,6 +414,8 @@ "@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="], + "@aws-sdk/client-athena": ["@aws-sdk/client-athena@3.1024.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.26", "@aws-sdk/credential-provider-node": "^3.972.29", "@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-kKnsdeh58Se7GL+9HX56KWtjS55W3OuzZwGVXq20PXgY2N53d6+NI9I1w+X0cZJo2pz3JijiJ+3S76YYCBoprw=="], + "@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=="], @@ -433,25 +438,25 @@ "@aws-sdk/client-sso": ["@aws-sdk/client-sso@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/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-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-SdqJGWVhmIURvCSgkDditHRO+ozubwZk9aCX9MK8qxyOndhobCndW1ozl3hX9psvMAo9Q4bppjuqy/GHWpjB+A=="], - "@aws-sdk/core": ["@aws-sdk/core@3.940.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws-sdk/xml-builder": "3.930.0", "@smithy/core": "^3.18.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KsGD2FLaX5ngJao1mHxodIVU9VYd1E8810fcYiGwO1PFHDzf5BEkp6D9IdMeQwT8Q6JLYtiiT1Y/o3UCScnGoA=="], + "@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/crc64-nvme": ["@aws-sdk/crc64-nvme@3.972.5", "", { "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-2VbTstbjKdT+yKi8m7b3a9CiVac+pL/IY2PHJwsaGkkHmuuqkJZIErPck1h6P3T9ghQMLSdMPyW6Qp7Di5swFg=="], - "@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-/G3l5/wbZYP2XEQiOoIkRJmlv15f1P3MSd1a0gz27lHEMrOJOGq66rF1Ca4OJLzapWt3Fy9BPrZAepoAX11kMw=="], + "@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/credential-provider-http": ["@aws-sdk/credential-provider-http@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/node-http-handler": "^4.4.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/util-stream": "^4.5.6", "tslib": "^2.6.2" } }, "sha512-dOrc03DHElNBD6N9Okt4U0zhrG4Wix5QUBSZPr5VN8SvmjD9dkrrxOkkJaMCl/bzrW7kbQEp7LuBdbxArMmOZQ=="], + "@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/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/credential-provider-env": "3.940.0", "@aws-sdk/credential-provider-http": "3.940.0", "@aws-sdk/credential-provider-login": "3.940.0", "@aws-sdk/credential-provider-process": "3.940.0", "@aws-sdk/credential-provider-sso": "3.940.0", "@aws-sdk/credential-provider-web-identity": "3.940.0", "@aws-sdk/nested-clients": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-gn7PJQEzb/cnInNFTOaDoCN/hOKqMejNmLof1W5VW95Qk0TPO52lH8R4RmJPnRrwFMswOWswTOpR1roKNLIrcw=="], + "@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/credential-provider-login": ["@aws-sdk/credential-provider-login@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/nested-clients": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-fOKC3VZkwa9T2l2VFKWRtfHQPQuISqqNl35ZhcXjWKVwRwl/o7THPMkqI4XwgT2noGa7LLYVbWMwnsgSsBqglg=="], + "@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/credential-provider-node": ["@aws-sdk/credential-provider-node@3.940.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.940.0", "@aws-sdk/credential-provider-http": "3.940.0", "@aws-sdk/credential-provider-ini": "3.940.0", "@aws-sdk/credential-provider-process": "3.940.0", "@aws-sdk/credential-provider-sso": "3.940.0", "@aws-sdk/credential-provider-web-identity": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-M8NFAvgvO6xZjiti5kztFiAYmSmSlG3eUfr4ZHSfXYZUA/KUdZU/D6xJyaLnU8cYRWBludb6K9XPKKVwKfqm4g=="], + "@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/credential-provider-process": ["@aws-sdk/credential-provider-process@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-pILBzt5/TYCqRsJb7vZlxmRIe0/T+FZPeml417EK75060ajDGnVJjHcuVdLVIeKoTKm9gmJc9l45gon6PbHyUQ=="], + "@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/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.940.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.940.0", "@aws-sdk/core": "3.940.0", "@aws-sdk/token-providers": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-q6JMHIkBlDCOMnA3RAzf8cGfup+8ukhhb50fNpghMs1SNBGhanmaMbZSgLigBRsPQW7fOk2l8jnzdVLS+BB9Uw=="], + "@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/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/nested-clients": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-9QLTIkDJHHaYL0nyymO41H8g3ui1yz6Y3GmAN1gYQa6plXisuFBnGAbmKVj7zNvjWaOKdF0dV3dd3AFKEDoJ/w=="], + "@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/endpoint-cache": ["@aws-sdk/endpoint-cache@3.893.0", "", { "dependencies": { "mnemonist": "0.38.3", "tslib": "^2.6.2" } }, "sha512-KSwTfyLZyNLszz5f/yoLC+LC+CRKpeJii/+zVAy7JUOQsKhSykiRUPYUx7o2Sdc4oJfqqUl26A/jSttKYnYtAA=="], @@ -469,13 +474,13 @@ "@aws-sdk/middleware-flexible-checksums": ["@aws-sdk/middleware-flexible-checksums@3.974.4", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", "@aws-sdk/core": "^3.973.24", "@aws-sdk/crc64-nvme": "^3.972.5", "@aws-sdk/types": "^3.973.6", "@smithy/is-array-buffer": "^4.2.2", "@smithy/node-config-provider": "^4.3.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-middleware": "^4.2.12", "@smithy/util-stream": "^4.5.20", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-fhCbZXPAyy8btnNbnBlR7Cc1nD54cETSvGn2wey71ehsM89AKPO8Dpco9DBAAgvrUdLrdHQepBXcyX4vxC5OwA=="], - "@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw=="], + "@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/middleware-location-constraint": ["@aws-sdk/middleware-location-constraint@3.972.8", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-KaUoFuoFPziIa98DSQsTPeke1gvGXlc5ZGMhy+b+nLxZ4A7jmJgLzjEF95l8aOQN2T/qlPP3MrAyELm8ExXucw=="], - "@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw=="], + "@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/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws/lambda-invoke-store": "^0.2.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-l4aGbHpXM45YNgXggIux1HgsCVAvvBoqHPkqLnqMl9QVapfuSTjJHfDYDsx1Xxct6/m7qSMUzanBALhiaGO2fA=="], + "@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/middleware-sdk-s3": ["@aws-sdk/middleware-sdk-s3@3.972.24", "", { "dependencies": { "@aws-sdk/core": "^3.973.24", "@aws-sdk/types": "^3.973.6", "@aws-sdk/util-arn-parser": "^3.972.3", "@smithy/core": "^3.23.12", "@smithy/node-config-provider": "^4.3.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-config-provider": "^4.2.2", "@smithy/util-middleware": "^4.2.12", "@smithy/util-stream": "^4.5.20", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-4sXxVC/enYgMkZefNMOzU6C6KtAXEvwVJLgNcUx1dvROH6GvKB5Sm2RGnGzTp0/PwkibIyMw4kOzF8tbLfaBAQ=="], @@ -483,13 +488,13 @@ "@aws-sdk/middleware-ssec": ["@aws-sdk/middleware-ssec@3.972.8", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-wqlK0yO/TxEC2UsY9wIlqeeutF6jjLe0f96Pbm40XscTo57nImUk9lBcw0dPgsm0sppFtAkSlDrfpK+pC30Wqw=="], - "@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@smithy/core": "^3.18.5", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-nJbLrUj6fY+l2W2rIB9P4Qvpiy0tnTdg/dmixRxrU1z3e8wBdspJlyE+AZN4fuVbeL6rrRrO/zxQC1bB3cw5IA=="], + "@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/middleware-websocket": ["@aws-sdk/middleware-websocket@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws-sdk/util-format-url": "3.936.0", "@smithy/eventstream-codec": "^4.2.5", "@smithy/eventstream-serde-browser": "^4.2.5", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/types": "^4.9.0", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-bPe3rqeugyj/MmjP0yBSZox2v1Wa8Dv39KN+RxVbQroLO8VUitBo6xyZ0oZebhZ5sASwSg58aDcMlX0uFLQnTA=="], "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@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/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-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-x0mdv6DkjXqXEcQj3URbCltEzW6hoy/1uIL+i8gExP6YKrnhiZ7SzuB4gPls2UOpK5UqLiqXjhRLfBb1C9i4Dw=="], - "@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/config-resolver": "^4.4.3", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw=="], + "@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/s3-request-presigner": ["@aws-sdk/s3-request-presigner@3.1015.0", "", { "dependencies": { "@aws-sdk/signature-v4-multi-region": "^3.996.12", "@aws-sdk/types": "^3.973.6", "@aws-sdk/util-format-url": "^3.972.8", "@smithy/middleware-endpoint": "^4.4.27", "@smithy/protocol-http": "^5.3.12", "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-N8Axxt3VNXPPnujakUfwm5SvyoE+4dqeIdfPr2EXLgV8vruerHuH9fb9/Dr1lGYeaRjM161ye2d3Ko4TB7oZLg=="], @@ -497,23 +502,23 @@ "@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/nested-clients": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-k5qbRe/ZFjW9oWEdzLIa2twRVIEx7p/9rutofyrRysrtEnYh3HAWCngAnwbgKMoiwa806UzcTRx0TjyEpnKcCg=="], - "@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="], + "@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/util-arn-parser": ["@aws-sdk/util-arn-parser@3.972.3", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA=="], "@aws-sdk/util-dynamodb": ["@aws-sdk/util-dynamodb@3.940.0", "", { "dependencies": { "tslib": "^2.6.2" }, "peerDependencies": { "@aws-sdk/client-dynamodb": "^3.940.0" } }, "sha512-T8UTYtCYSPxktnk68fKBdWztnqdTQItJwi/8N9lsvp20alJ15wCQsvQR+GKB5p4TCKxOPyNEirkcrNlf5TKppA=="], - "@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-endpoints": "^3.2.5", "tslib": "^2.6.2" } }, "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w=="], + "@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/util-format-url": ["@aws-sdk/util-format-url@3.972.8", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@smithy/querystring-builder": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-J6DS9oocrgxM8xlUTTmQOuwRF6rnAGEujAN9SAzllcrQmwn5iJ58ogxy3SEhD0Q7JZvlA5jvIXBkpQRqEqlE9A=="], "@aws-sdk/util-locate-window": ["@aws-sdk/util-locate-window@3.965.5", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ=="], - "@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw=="], + "@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/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.940.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-dlD/F+L/jN26I8Zg5x0oDGJiA+/WEQmnSE27fi5ydvYnpfQLwThtQo9SsNS47XSR/SOULaaoC9qx929rZuo74A=="], + "@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/xml-builder": ["@aws-sdk/xml-builder@3.930.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA=="], + "@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/lambda-invoke-store": ["@aws/lambda-invoke-store@0.2.4", "", {}, "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ=="], @@ -1325,7 +1330,7 @@ "@smithy/config-resolver": ["@smithy/config-resolver@4.4.13", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.12", "@smithy/types": "^4.13.1", "@smithy/util-config-provider": "^4.2.2", "@smithy/util-endpoints": "^3.3.3", "@smithy/util-middleware": "^4.2.12", "tslib": "^2.6.2" } }, "sha512-iIzMC5NmOUP6WL6o8iPBjFhUhBZ9pPjpUpQYWMUFQqKyXXzOftbfK8zcQCz/jFV1Psmf05BK5ypx4K2r4Tnwdg=="], - "@smithy/core": ["@smithy/core@3.23.12", "", { "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.20", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w=="], + "@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=="], "@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.12", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.12", "@smithy/property-provider": "^4.2.12", "@smithy/types": "^4.13.1", "@smithy/url-parser": "^4.2.12", "tslib": "^2.6.2" } }, "sha512-cr2lR792vNZcYMriSIj+Um3x9KWrjcu98kn234xA6reOAFMmbRpQMOv8KPgEmLLtx3eldU6c5wALKFqNOhugmg=="], @@ -1357,17 +1362,17 @@ "@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.12", "", { "dependencies": { "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-YE58Yz+cvFInWI/wOTrB+DbvUVz/pLn5mC5MvOV4fdRUc6qGwygyngcucRQjAhiCEbmfLOXX0gntSIcgMvAjmA=="], - "@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.27", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-serde": "^4.2.15", "@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-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA=="], + "@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=="], - "@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.44", "", { "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.7", "@smithy/types": "^4.13.1", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.12", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-Y1Rav7m5CFRPQyM4CI0koD/bXjyjJu3EQxZZhtLGD88WIrBrQ7kqXM96ncd6rYnojwOo/u9MXu57JrEvu/nLrA=="], + "@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=="], - "@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.15", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg=="], + "@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=="], "@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.12", "", { "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-kruC5gRHwsCOuyCd4ouQxYjgRAym2uDlCvQ5acuMtRrcdfg7mFBg6blaxcJ09STpt3ziEkis6bhg1uwrWU7txw=="], "@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.12", "", { "dependencies": { "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-tr2oKX2xMcO+rBOjobSwVAkV05SIfUKz8iI53rzxEmgW3GOOPOv0UioSDk+J8OpRQnpnhsO3Af6IEBabQBVmiw=="], - "@smithy/node-http-handler": ["@smithy/node-http-handler@4.5.0", "", { "dependencies": { "@smithy/abort-controller": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/querystring-builder": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Rnq9vQWiR1+/I6NZZMNzJHV6pZYyEHt2ZnuV3MG8z2NNenC4i/8Kzttz7CjZiHSmsN5frhXhg17z3Zqjjhmz1A=="], + "@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=="], "@smithy/property-provider": ["@smithy/property-provider@4.2.12", "", { "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-jqve46eYU1v7pZ5BM+fmkbq3DerkSluPr5EhvOcHxygxzD05ByDRppRwRPPpFrsFo5yDtCYLKu+kreHKVrvc7A=="], @@ -1383,7 +1388,7 @@ "@smithy/signature-v4": ["@smithy/signature-v4@5.3.12", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.2", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-hex-encoding": "^4.2.2", "@smithy/util-middleware": "^4.2.12", "@smithy/util-uri-escape": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-B/FBwO3MVOL00DaRSXfXfa/TRXRheagt/q5A2NM13u7q+sHS59EOVGQNfG7DkmVtdQm5m3vOosoKAXSqn/OEgw=="], - "@smithy/smithy-client": ["@smithy/smithy-client@4.12.7", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-endpoint": "^4.4.27", "@smithy/middleware-stack": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-stream": "^4.5.20", "tslib": "^2.6.2" } }, "sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ=="], + "@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=="], "@smithy/types": ["@smithy/types@4.13.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-787F3yzE2UiJIQ+wYW1CVg2odHjmaWLGksnKQHUrK/lYZSEcy1msuLVvxaR/sI2/aDe9U+TBuLsXnr3vod1g0g=="], @@ -1399,9 +1404,9 @@ "@smithy/util-config-provider": ["@smithy/util-config-provider@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ=="], - "@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.43", "", { "dependencies": { "@smithy/property-provider": "^4.2.12", "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Qd/0wCKMaXxev/z00TvNzGCH2jlKKKxXP1aDxB6oKwSQthe3Og2dMhSayGCnsma1bK/kQX1+X7SMP99t6FgiiQ=="], + "@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=="], - "@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.47", "", { "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.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-qSRbYp1EQ7th+sPFuVcVO05AE0QH635hycdEXlpzIahqHHf2Fyd/Zl+8v0XYMJ3cgDVPa0lkMefU7oNUjAP+DQ=="], + "@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=="], "@smithy/util-endpoints": ["@smithy/util-endpoints@3.3.3", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-VACQVe50j0HZPjpwWcjyT51KUQ4AnsvEaQ2lKHOSL4mNLD0G9BjEniQ+yCt1qqfKfiAHRAts26ud7hBjamrwig=="], @@ -1409,7 +1414,7 @@ "@smithy/util-middleware": ["@smithy/util-middleware@4.2.12", "", { "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Er805uFUOvgc0l8nv0e0su0VFISoxhJ/AwOn3gL2NWNY2LUEldP5WtVcRYSQBcjg0y9NfG8JYrCJaYDpupBHJQ=="], - "@smithy/util-retry": ["@smithy/util-retry@4.2.12", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ=="], + "@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=="], "@smithy/util-stream": ["@smithy/util-stream@4.5.20", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.15", "@smithy/node-http-handler": "^4.5.0", "@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-4yXLm5n/B5SRBR2p8cZ90Sbv4zL4NKsgxdzCzp/83cXw2KxLEumt5p+GAVyRNZgQOSrzXn9ARpO0lUe8XSlSDw=="], @@ -3833,71 +3838,215 @@ "@authenio/xml-encryption/xpath": ["xpath@0.0.32", "", {}, "sha512-rxMJhSIoiO8vXcWvSifKqhvV96GjiD5wYb8/QHdoRyQvraTpp4IEv944nhGausZZ3u7dhQXteZuZbaqfpB7uYw=="], - "@aws-crypto/crc32/@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-crypto/sha1-browser/@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-crypto/crc32c/@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-crypto/sha256-browser/@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-crypto/sha1-browser/@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-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-crypto/sha1-browser/@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-bedrock-runtime/@aws-sdk/core": ["@aws-sdk/core@3.940.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws-sdk/xml-builder": "3.930.0", "@smithy/core": "^3.18.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KsGD2FLaX5ngJao1mHxodIVU9VYd1E8810fcYiGwO1PFHDzf5BEkp6D9IdMeQwT8Q6JLYtiiT1Y/o3UCScnGoA=="], - "@aws-crypto/sha256-browser/@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-bedrock-runtime/@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.940.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.940.0", "@aws-sdk/credential-provider-http": "3.940.0", "@aws-sdk/credential-provider-ini": "3.940.0", "@aws-sdk/credential-provider-process": "3.940.0", "@aws-sdk/credential-provider-sso": "3.940.0", "@aws-sdk/credential-provider-web-identity": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-M8NFAvgvO6xZjiti5kztFiAYmSmSlG3eUfr4ZHSfXYZUA/KUdZU/D6xJyaLnU8cYRWBludb6K9XPKKVwKfqm4g=="], - "@aws-crypto/sha256-browser/@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-bedrock-runtime/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw=="], - "@aws-crypto/sha256-js/@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-bedrock-runtime/@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw=="], - "@aws-crypto/util/@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-bedrock-runtime/@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws/lambda-invoke-store": "^0.2.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-l4aGbHpXM45YNgXggIux1HgsCVAvvBoqHPkqLnqMl9QVapfuSTjJHfDYDsx1Xxct6/m7qSMUzanBALhiaGO2fA=="], - "@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-bedrock-runtime/@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@smithy/core": "^3.18.5", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-nJbLrUj6fY+l2W2rIB9P4Qvpiy0tnTdg/dmixRxrU1z3e8wBdspJlyE+AZN4fuVbeL6rrRrO/zxQC1bB3cw5IA=="], - "@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-bedrock-runtime/@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/config-resolver": "^4.4.3", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw=="], - "@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-bedrock-runtime/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="], - "@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-bedrock-runtime/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-endpoints": "^3.2.5", "tslib": "^2.6.2" } }, "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w=="], - "@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-bedrock-runtime/@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw=="], - "@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-bedrock-runtime/@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.940.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-dlD/F+L/jN26I8Zg5x0oDGJiA+/WEQmnSE27fi5ydvYnpfQLwThtQo9SsNS47XSR/SOULaaoC9qx929rZuo74A=="], - "@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-bedrock-runtime/@smithy/core": ["@smithy/core@3.23.12", "", { "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.20", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w=="], - "@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-bedrock-runtime/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.27", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-serde": "^4.2.15", "@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-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA=="], - "@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-bedrock-runtime/@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.44", "", { "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.7", "@smithy/types": "^4.13.1", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.12", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-Y1Rav7m5CFRPQyM4CI0koD/bXjyjJu3EQxZZhtLGD88WIrBrQ7kqXM96ncd6rYnojwOo/u9MXu57JrEvu/nLrA=="], - "@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-bedrock-runtime/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.15", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg=="], - "@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-bedrock-runtime/@smithy/node-http-handler": ["@smithy/node-http-handler@4.5.0", "", { "dependencies": { "@smithy/abort-controller": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/querystring-builder": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Rnq9vQWiR1+/I6NZZMNzJHV6pZYyEHt2ZnuV3MG8z2NNenC4i/8Kzttz7CjZiHSmsN5frhXhg17z3Zqjjhmz1A=="], - "@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-bedrock-runtime/@smithy/smithy-client": ["@smithy/smithy-client@4.12.7", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-endpoint": "^4.4.27", "@smithy/middleware-stack": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-stream": "^4.5.20", "tslib": "^2.6.2" } }, "sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ=="], - "@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-bedrock-runtime/@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.43", "", { "dependencies": { "@smithy/property-provider": "^4.2.12", "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Qd/0wCKMaXxev/z00TvNzGCH2jlKKKxXP1aDxB6oKwSQthe3Og2dMhSayGCnsma1bK/kQX1+X7SMP99t6FgiiQ=="], - "@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-bedrock-runtime/@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.47", "", { "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.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-qSRbYp1EQ7th+sPFuVcVO05AE0QH635hycdEXlpzIahqHHf2Fyd/Zl+8v0XYMJ3cgDVPa0lkMefU7oNUjAP+DQ=="], - "@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-bedrock-runtime/@smithy/util-retry": ["@smithy/util-retry@4.2.12", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ=="], - "@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-cloudwatch/@aws-sdk/core": ["@aws-sdk/core@3.940.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws-sdk/xml-builder": "3.930.0", "@smithy/core": "^3.18.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KsGD2FLaX5ngJao1mHxodIVU9VYd1E8810fcYiGwO1PFHDzf5BEkp6D9IdMeQwT8Q6JLYtiiT1Y/o3UCScnGoA=="], - "@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-cloudwatch/@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.940.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.940.0", "@aws-sdk/credential-provider-http": "3.940.0", "@aws-sdk/credential-provider-ini": "3.940.0", "@aws-sdk/credential-provider-process": "3.940.0", "@aws-sdk/credential-provider-sso": "3.940.0", "@aws-sdk/credential-provider-web-identity": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-M8NFAvgvO6xZjiti5kztFiAYmSmSlG3eUfr4ZHSfXYZUA/KUdZU/D6xJyaLnU8cYRWBludb6K9XPKKVwKfqm4g=="], - "@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-cloudwatch/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw=="], - "@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-cloudwatch/@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw=="], - "@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-cloudwatch/@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws/lambda-invoke-store": "^0.2.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-l4aGbHpXM45YNgXggIux1HgsCVAvvBoqHPkqLnqMl9QVapfuSTjJHfDYDsx1Xxct6/m7qSMUzanBALhiaGO2fA=="], - "@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-cloudwatch/@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@smithy/core": "^3.18.5", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-nJbLrUj6fY+l2W2rIB9P4Qvpiy0tnTdg/dmixRxrU1z3e8wBdspJlyE+AZN4fuVbeL6rrRrO/zxQC1bB3cw5IA=="], - "@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-cloudwatch/@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/config-resolver": "^4.4.3", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw=="], - "@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=="], + "@aws-sdk/client-cloudwatch/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="], + + "@aws-sdk/client-cloudwatch/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-endpoints": "^3.2.5", "tslib": "^2.6.2" } }, "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w=="], + + "@aws-sdk/client-cloudwatch/@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw=="], + + "@aws-sdk/client-cloudwatch/@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.940.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-dlD/F+L/jN26I8Zg5x0oDGJiA+/WEQmnSE27fi5ydvYnpfQLwThtQo9SsNS47XSR/SOULaaoC9qx929rZuo74A=="], + + "@aws-sdk/client-cloudwatch/@smithy/core": ["@smithy/core@3.23.12", "", { "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.20", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w=="], + + "@aws-sdk/client-cloudwatch/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.27", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-serde": "^4.2.15", "@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-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA=="], + + "@aws-sdk/client-cloudwatch/@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.44", "", { "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.7", "@smithy/types": "^4.13.1", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.12", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-Y1Rav7m5CFRPQyM4CI0koD/bXjyjJu3EQxZZhtLGD88WIrBrQ7kqXM96ncd6rYnojwOo/u9MXu57JrEvu/nLrA=="], + + "@aws-sdk/client-cloudwatch/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.15", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg=="], + + "@aws-sdk/client-cloudwatch/@smithy/node-http-handler": ["@smithy/node-http-handler@4.5.0", "", { "dependencies": { "@smithy/abort-controller": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/querystring-builder": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Rnq9vQWiR1+/I6NZZMNzJHV6pZYyEHt2ZnuV3MG8z2NNenC4i/8Kzttz7CjZiHSmsN5frhXhg17z3Zqjjhmz1A=="], + + "@aws-sdk/client-cloudwatch/@smithy/smithy-client": ["@smithy/smithy-client@4.12.7", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-endpoint": "^4.4.27", "@smithy/middleware-stack": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-stream": "^4.5.20", "tslib": "^2.6.2" } }, "sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ=="], + + "@aws-sdk/client-cloudwatch/@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.43", "", { "dependencies": { "@smithy/property-provider": "^4.2.12", "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Qd/0wCKMaXxev/z00TvNzGCH2jlKKKxXP1aDxB6oKwSQthe3Og2dMhSayGCnsma1bK/kQX1+X7SMP99t6FgiiQ=="], + + "@aws-sdk/client-cloudwatch/@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.47", "", { "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.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-qSRbYp1EQ7th+sPFuVcVO05AE0QH635hycdEXlpzIahqHHf2Fyd/Zl+8v0XYMJ3cgDVPa0lkMefU7oNUjAP+DQ=="], + + "@aws-sdk/client-cloudwatch/@smithy/util-retry": ["@smithy/util-retry@4.2.12", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ=="], + + "@aws-sdk/client-cloudwatch-logs/@aws-sdk/core": ["@aws-sdk/core@3.940.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws-sdk/xml-builder": "3.930.0", "@smithy/core": "^3.18.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KsGD2FLaX5ngJao1mHxodIVU9VYd1E8810fcYiGwO1PFHDzf5BEkp6D9IdMeQwT8Q6JLYtiiT1Y/o3UCScnGoA=="], + + "@aws-sdk/client-cloudwatch-logs/@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.940.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.940.0", "@aws-sdk/credential-provider-http": "3.940.0", "@aws-sdk/credential-provider-ini": "3.940.0", "@aws-sdk/credential-provider-process": "3.940.0", "@aws-sdk/credential-provider-sso": "3.940.0", "@aws-sdk/credential-provider-web-identity": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-M8NFAvgvO6xZjiti5kztFiAYmSmSlG3eUfr4ZHSfXYZUA/KUdZU/D6xJyaLnU8cYRWBludb6K9XPKKVwKfqm4g=="], + + "@aws-sdk/client-cloudwatch-logs/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw=="], + + "@aws-sdk/client-cloudwatch-logs/@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw=="], + + "@aws-sdk/client-cloudwatch-logs/@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws/lambda-invoke-store": "^0.2.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-l4aGbHpXM45YNgXggIux1HgsCVAvvBoqHPkqLnqMl9QVapfuSTjJHfDYDsx1Xxct6/m7qSMUzanBALhiaGO2fA=="], + + "@aws-sdk/client-cloudwatch-logs/@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@smithy/core": "^3.18.5", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-nJbLrUj6fY+l2W2rIB9P4Qvpiy0tnTdg/dmixRxrU1z3e8wBdspJlyE+AZN4fuVbeL6rrRrO/zxQC1bB3cw5IA=="], + + "@aws-sdk/client-cloudwatch-logs/@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/config-resolver": "^4.4.3", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw=="], + + "@aws-sdk/client-cloudwatch-logs/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="], + + "@aws-sdk/client-cloudwatch-logs/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-endpoints": "^3.2.5", "tslib": "^2.6.2" } }, "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w=="], + + "@aws-sdk/client-cloudwatch-logs/@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw=="], + + "@aws-sdk/client-cloudwatch-logs/@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.940.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-dlD/F+L/jN26I8Zg5x0oDGJiA+/WEQmnSE27fi5ydvYnpfQLwThtQo9SsNS47XSR/SOULaaoC9qx929rZuo74A=="], + + "@aws-sdk/client-cloudwatch-logs/@smithy/core": ["@smithy/core@3.23.12", "", { "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.20", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w=="], + + "@aws-sdk/client-cloudwatch-logs/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.27", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-serde": "^4.2.15", "@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-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA=="], + + "@aws-sdk/client-cloudwatch-logs/@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.44", "", { "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.7", "@smithy/types": "^4.13.1", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.12", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-Y1Rav7m5CFRPQyM4CI0koD/bXjyjJu3EQxZZhtLGD88WIrBrQ7kqXM96ncd6rYnojwOo/u9MXu57JrEvu/nLrA=="], + + "@aws-sdk/client-cloudwatch-logs/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.15", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg=="], + + "@aws-sdk/client-cloudwatch-logs/@smithy/node-http-handler": ["@smithy/node-http-handler@4.5.0", "", { "dependencies": { "@smithy/abort-controller": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/querystring-builder": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Rnq9vQWiR1+/I6NZZMNzJHV6pZYyEHt2ZnuV3MG8z2NNenC4i/8Kzttz7CjZiHSmsN5frhXhg17z3Zqjjhmz1A=="], + + "@aws-sdk/client-cloudwatch-logs/@smithy/smithy-client": ["@smithy/smithy-client@4.12.7", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-endpoint": "^4.4.27", "@smithy/middleware-stack": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-stream": "^4.5.20", "tslib": "^2.6.2" } }, "sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ=="], + + "@aws-sdk/client-cloudwatch-logs/@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.43", "", { "dependencies": { "@smithy/property-provider": "^4.2.12", "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Qd/0wCKMaXxev/z00TvNzGCH2jlKKKxXP1aDxB6oKwSQthe3Og2dMhSayGCnsma1bK/kQX1+X7SMP99t6FgiiQ=="], + + "@aws-sdk/client-cloudwatch-logs/@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.47", "", { "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.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-qSRbYp1EQ7th+sPFuVcVO05AE0QH635hycdEXlpzIahqHHf2Fyd/Zl+8v0XYMJ3cgDVPa0lkMefU7oNUjAP+DQ=="], + + "@aws-sdk/client-cloudwatch-logs/@smithy/util-retry": ["@smithy/util-retry@4.2.12", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ=="], + + "@aws-sdk/client-dynamodb/@aws-sdk/core": ["@aws-sdk/core@3.940.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws-sdk/xml-builder": "3.930.0", "@smithy/core": "^3.18.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KsGD2FLaX5ngJao1mHxodIVU9VYd1E8810fcYiGwO1PFHDzf5BEkp6D9IdMeQwT8Q6JLYtiiT1Y/o3UCScnGoA=="], + + "@aws-sdk/client-dynamodb/@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.940.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.940.0", "@aws-sdk/credential-provider-http": "3.940.0", "@aws-sdk/credential-provider-ini": "3.940.0", "@aws-sdk/credential-provider-process": "3.940.0", "@aws-sdk/credential-provider-sso": "3.940.0", "@aws-sdk/credential-provider-web-identity": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-M8NFAvgvO6xZjiti5kztFiAYmSmSlG3eUfr4ZHSfXYZUA/KUdZU/D6xJyaLnU8cYRWBludb6K9XPKKVwKfqm4g=="], + + "@aws-sdk/client-dynamodb/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw=="], + + "@aws-sdk/client-dynamodb/@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw=="], + + "@aws-sdk/client-dynamodb/@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws/lambda-invoke-store": "^0.2.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-l4aGbHpXM45YNgXggIux1HgsCVAvvBoqHPkqLnqMl9QVapfuSTjJHfDYDsx1Xxct6/m7qSMUzanBALhiaGO2fA=="], + + "@aws-sdk/client-dynamodb/@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@smithy/core": "^3.18.5", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-nJbLrUj6fY+l2W2rIB9P4Qvpiy0tnTdg/dmixRxrU1z3e8wBdspJlyE+AZN4fuVbeL6rrRrO/zxQC1bB3cw5IA=="], + + "@aws-sdk/client-dynamodb/@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/config-resolver": "^4.4.3", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw=="], + + "@aws-sdk/client-dynamodb/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="], + + "@aws-sdk/client-dynamodb/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-endpoints": "^3.2.5", "tslib": "^2.6.2" } }, "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w=="], + + "@aws-sdk/client-dynamodb/@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw=="], + + "@aws-sdk/client-dynamodb/@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.940.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-dlD/F+L/jN26I8Zg5x0oDGJiA+/WEQmnSE27fi5ydvYnpfQLwThtQo9SsNS47XSR/SOULaaoC9qx929rZuo74A=="], + + "@aws-sdk/client-dynamodb/@smithy/core": ["@smithy/core@3.23.12", "", { "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.20", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w=="], + + "@aws-sdk/client-dynamodb/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.27", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-serde": "^4.2.15", "@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-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA=="], + + "@aws-sdk/client-dynamodb/@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.44", "", { "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.7", "@smithy/types": "^4.13.1", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.12", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-Y1Rav7m5CFRPQyM4CI0koD/bXjyjJu3EQxZZhtLGD88WIrBrQ7kqXM96ncd6rYnojwOo/u9MXu57JrEvu/nLrA=="], + + "@aws-sdk/client-dynamodb/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.15", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg=="], + + "@aws-sdk/client-dynamodb/@smithy/node-http-handler": ["@smithy/node-http-handler@4.5.0", "", { "dependencies": { "@smithy/abort-controller": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/querystring-builder": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Rnq9vQWiR1+/I6NZZMNzJHV6pZYyEHt2ZnuV3MG8z2NNenC4i/8Kzttz7CjZiHSmsN5frhXhg17z3Zqjjhmz1A=="], + + "@aws-sdk/client-dynamodb/@smithy/smithy-client": ["@smithy/smithy-client@4.12.7", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-endpoint": "^4.4.27", "@smithy/middleware-stack": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-stream": "^4.5.20", "tslib": "^2.6.2" } }, "sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ=="], + + "@aws-sdk/client-dynamodb/@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.43", "", { "dependencies": { "@smithy/property-provider": "^4.2.12", "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Qd/0wCKMaXxev/z00TvNzGCH2jlKKKxXP1aDxB6oKwSQthe3Og2dMhSayGCnsma1bK/kQX1+X7SMP99t6FgiiQ=="], + + "@aws-sdk/client-dynamodb/@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.47", "", { "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.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-qSRbYp1EQ7th+sPFuVcVO05AE0QH635hycdEXlpzIahqHHf2Fyd/Zl+8v0XYMJ3cgDVPa0lkMefU7oNUjAP+DQ=="], + + "@aws-sdk/client-dynamodb/@smithy/util-retry": ["@smithy/util-retry@4.2.12", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ=="], - "@aws-sdk/client-s3/@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-rds-data/@aws-sdk/core": ["@aws-sdk/core@3.940.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws-sdk/xml-builder": "3.930.0", "@smithy/core": "^3.18.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KsGD2FLaX5ngJao1mHxodIVU9VYd1E8810fcYiGwO1PFHDzf5BEkp6D9IdMeQwT8Q6JLYtiiT1Y/o3UCScnGoA=="], - "@aws-sdk/client-s3/@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-rds-data/@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.940.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.940.0", "@aws-sdk/credential-provider-http": "3.940.0", "@aws-sdk/credential-provider-ini": "3.940.0", "@aws-sdk/credential-provider-process": "3.940.0", "@aws-sdk/credential-provider-sso": "3.940.0", "@aws-sdk/credential-provider-web-identity": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-M8NFAvgvO6xZjiti5kztFiAYmSmSlG3eUfr4ZHSfXYZUA/KUdZU/D6xJyaLnU8cYRWBludb6K9XPKKVwKfqm4g=="], + + "@aws-sdk/client-rds-data/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw=="], + + "@aws-sdk/client-rds-data/@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw=="], + + "@aws-sdk/client-rds-data/@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws/lambda-invoke-store": "^0.2.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-l4aGbHpXM45YNgXggIux1HgsCVAvvBoqHPkqLnqMl9QVapfuSTjJHfDYDsx1Xxct6/m7qSMUzanBALhiaGO2fA=="], + + "@aws-sdk/client-rds-data/@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@smithy/core": "^3.18.5", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-nJbLrUj6fY+l2W2rIB9P4Qvpiy0tnTdg/dmixRxrU1z3e8wBdspJlyE+AZN4fuVbeL6rrRrO/zxQC1bB3cw5IA=="], + + "@aws-sdk/client-rds-data/@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/config-resolver": "^4.4.3", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw=="], + + "@aws-sdk/client-rds-data/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="], + + "@aws-sdk/client-rds-data/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-endpoints": "^3.2.5", "tslib": "^2.6.2" } }, "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w=="], + + "@aws-sdk/client-rds-data/@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw=="], + + "@aws-sdk/client-rds-data/@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.940.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-dlD/F+L/jN26I8Zg5x0oDGJiA+/WEQmnSE27fi5ydvYnpfQLwThtQo9SsNS47XSR/SOULaaoC9qx929rZuo74A=="], + + "@aws-sdk/client-rds-data/@smithy/core": ["@smithy/core@3.23.12", "", { "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.20", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w=="], + + "@aws-sdk/client-rds-data/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.27", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-serde": "^4.2.15", "@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-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA=="], + + "@aws-sdk/client-rds-data/@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.44", "", { "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.7", "@smithy/types": "^4.13.1", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.12", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-Y1Rav7m5CFRPQyM4CI0koD/bXjyjJu3EQxZZhtLGD88WIrBrQ7kqXM96ncd6rYnojwOo/u9MXu57JrEvu/nLrA=="], + + "@aws-sdk/client-rds-data/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.15", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg=="], + + "@aws-sdk/client-rds-data/@smithy/node-http-handler": ["@smithy/node-http-handler@4.5.0", "", { "dependencies": { "@smithy/abort-controller": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/querystring-builder": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Rnq9vQWiR1+/I6NZZMNzJHV6pZYyEHt2ZnuV3MG8z2NNenC4i/8Kzttz7CjZiHSmsN5frhXhg17z3Zqjjhmz1A=="], + + "@aws-sdk/client-rds-data/@smithy/smithy-client": ["@smithy/smithy-client@4.12.7", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-endpoint": "^4.4.27", "@smithy/middleware-stack": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-stream": "^4.5.20", "tslib": "^2.6.2" } }, "sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ=="], + + "@aws-sdk/client-rds-data/@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.43", "", { "dependencies": { "@smithy/property-provider": "^4.2.12", "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Qd/0wCKMaXxev/z00TvNzGCH2jlKKKxXP1aDxB6oKwSQthe3Og2dMhSayGCnsma1bK/kQX1+X7SMP99t6FgiiQ=="], + + "@aws-sdk/client-rds-data/@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.47", "", { "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.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-qSRbYp1EQ7th+sPFuVcVO05AE0QH635hycdEXlpzIahqHHf2Fyd/Zl+8v0XYMJ3cgDVPa0lkMefU7oNUjAP+DQ=="], + + "@aws-sdk/client-rds-data/@smithy/util-retry": ["@smithy/util-retry@4.2.12", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ=="], + + "@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=="], "@aws-sdk/client-s3/@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.972.8", "", { "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-BnnvYs2ZEpdlmZ2PNlV2ZyQ8j8AEkMTjN79y/YA475ER1ByFYrkVR85qmhni8oeTaJcDqbx364wDpitDAA/wCA=="], @@ -3905,39 +4054,51 @@ "@aws-sdk/client-s3/@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.972.9", "", { "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-eQ+dFU05ZRC/lC2XpYlYSPlXtX3VT8sn5toxN2Fv7EXlMoA2p9V7vUBKqHunfD4TRLpxUq8Y8Ol/nCqiv327Ng=="], - "@aws-sdk/client-s3/@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-s3/@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.973.11", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "^3.972.25", "@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-1qdXbXo2s5MMLpUvw00284LsbhtlQ4ul7Zzdn5n+7p4WVgCMLqhxImpHIrjSoc72E/fyc4Wq8dLtUld2Gsh+lA=="], - "@aws-sdk/client-s3/@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-s3/@smithy/core": ["@smithy/core@3.23.12", "", { "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.20", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w=="], - "@aws-sdk/client-s3/@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-s3/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.27", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-serde": "^4.2.15", "@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-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA=="], - "@aws-sdk/client-s3/@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.973.11", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "^3.972.25", "@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-1qdXbXo2s5MMLpUvw00284LsbhtlQ4ul7Zzdn5n+7p4WVgCMLqhxImpHIrjSoc72E/fyc4Wq8dLtUld2Gsh+lA=="], + "@aws-sdk/client-s3/@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.44", "", { "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.7", "@smithy/types": "^4.13.1", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.12", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-Y1Rav7m5CFRPQyM4CI0koD/bXjyjJu3EQxZZhtLGD88WIrBrQ7kqXM96ncd6rYnojwOo/u9MXu57JrEvu/nLrA=="], - "@aws-sdk/client-secrets-manager/@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-s3/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.15", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg=="], - "@aws-sdk/client-secrets-manager/@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-s3/@smithy/node-http-handler": ["@smithy/node-http-handler@4.5.0", "", { "dependencies": { "@smithy/abort-controller": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/querystring-builder": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Rnq9vQWiR1+/I6NZZMNzJHV6pZYyEHt2ZnuV3MG8z2NNenC4i/8Kzttz7CjZiHSmsN5frhXhg17z3Zqjjhmz1A=="], - "@aws-sdk/client-secrets-manager/@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-s3/@smithy/smithy-client": ["@smithy/smithy-client@4.12.7", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-endpoint": "^4.4.27", "@smithy/middleware-stack": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-stream": "^4.5.20", "tslib": "^2.6.2" } }, "sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ=="], - "@aws-sdk/client-secrets-manager/@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-s3/@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.43", "", { "dependencies": { "@smithy/property-provider": "^4.2.12", "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Qd/0wCKMaXxev/z00TvNzGCH2jlKKKxXP1aDxB6oKwSQthe3Og2dMhSayGCnsma1bK/kQX1+X7SMP99t6FgiiQ=="], - "@aws-sdk/client-secrets-manager/@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-s3/@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.47", "", { "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.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-qSRbYp1EQ7th+sPFuVcVO05AE0QH635hycdEXlpzIahqHHf2Fyd/Zl+8v0XYMJ3cgDVPa0lkMefU7oNUjAP+DQ=="], - "@aws-sdk/client-secrets-manager/@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-s3/@smithy/util-retry": ["@smithy/util-retry@4.2.12", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ=="], - "@aws-sdk/client-secrets-manager/@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-secrets-manager/@aws-sdk/core": ["@aws-sdk/core@3.940.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws-sdk/xml-builder": "3.930.0", "@smithy/core": "^3.18.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KsGD2FLaX5ngJao1mHxodIVU9VYd1E8810fcYiGwO1PFHDzf5BEkp6D9IdMeQwT8Q6JLYtiiT1Y/o3UCScnGoA=="], - "@aws-sdk/client-secrets-manager/@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-secrets-manager/@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.940.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.940.0", "@aws-sdk/credential-provider-http": "3.940.0", "@aws-sdk/credential-provider-ini": "3.940.0", "@aws-sdk/credential-provider-process": "3.940.0", "@aws-sdk/credential-provider-sso": "3.940.0", "@aws-sdk/credential-provider-web-identity": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-M8NFAvgvO6xZjiti5kztFiAYmSmSlG3eUfr4ZHSfXYZUA/KUdZU/D6xJyaLnU8cYRWBludb6K9XPKKVwKfqm4g=="], - "@aws-sdk/client-secrets-manager/@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-secrets-manager/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw=="], - "@aws-sdk/client-sesv2/@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-secrets-manager/@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw=="], - "@aws-sdk/client-sesv2/@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=="], + "@aws-sdk/client-secrets-manager/@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws/lambda-invoke-store": "^0.2.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-l4aGbHpXM45YNgXggIux1HgsCVAvvBoqHPkqLnqMl9QVapfuSTjJHfDYDsx1Xxct6/m7qSMUzanBALhiaGO2fA=="], + + "@aws-sdk/client-secrets-manager/@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@smithy/core": "^3.18.5", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-nJbLrUj6fY+l2W2rIB9P4Qvpiy0tnTdg/dmixRxrU1z3e8wBdspJlyE+AZN4fuVbeL6rrRrO/zxQC1bB3cw5IA=="], + + "@aws-sdk/client-secrets-manager/@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/config-resolver": "^4.4.3", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw=="], - "@aws-sdk/client-sesv2/@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-secrets-manager/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="], - "@aws-sdk/client-sesv2/@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-secrets-manager/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-endpoints": "^3.2.5", "tslib": "^2.6.2" } }, "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w=="], + + "@aws-sdk/client-secrets-manager/@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw=="], + + "@aws-sdk/client-secrets-manager/@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.940.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-dlD/F+L/jN26I8Zg5x0oDGJiA+/WEQmnSE27fi5ydvYnpfQLwThtQo9SsNS47XSR/SOULaaoC9qx929rZuo74A=="], + + "@aws-sdk/client-sesv2/@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-sesv2/@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=="], "@aws-sdk/client-sesv2/@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.972.8", "", { "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-BnnvYs2ZEpdlmZ2PNlV2ZyQ8j8AEkMTjN79y/YA475ER1ByFYrkVR85qmhni8oeTaJcDqbx364wDpitDAA/wCA=="], @@ -3945,47 +4106,191 @@ "@aws-sdk/client-sesv2/@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.972.9", "", { "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-eQ+dFU05ZRC/lC2XpYlYSPlXtX3VT8sn5toxN2Fv7EXlMoA2p9V7vUBKqHunfD4TRLpxUq8Y8Ol/nCqiv327Ng=="], - "@aws-sdk/client-sesv2/@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-sesv2/@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.973.11", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "^3.972.25", "@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-1qdXbXo2s5MMLpUvw00284LsbhtlQ4ul7Zzdn5n+7p4WVgCMLqhxImpHIrjSoc72E/fyc4Wq8dLtUld2Gsh+lA=="], - "@aws-sdk/client-sesv2/@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-sesv2/@smithy/core": ["@smithy/core@3.23.12", "", { "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.20", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w=="], - "@aws-sdk/client-sesv2/@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-sesv2/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.27", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-serde": "^4.2.15", "@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-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA=="], - "@aws-sdk/client-sesv2/@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.973.11", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "^3.972.25", "@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-1qdXbXo2s5MMLpUvw00284LsbhtlQ4ul7Zzdn5n+7p4WVgCMLqhxImpHIrjSoc72E/fyc4Wq8dLtUld2Gsh+lA=="], + "@aws-sdk/client-sesv2/@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.44", "", { "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.7", "@smithy/types": "^4.13.1", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.12", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-Y1Rav7m5CFRPQyM4CI0koD/bXjyjJu3EQxZZhtLGD88WIrBrQ7kqXM96ncd6rYnojwOo/u9MXu57JrEvu/nLrA=="], + + "@aws-sdk/client-sesv2/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.15", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg=="], + + "@aws-sdk/client-sesv2/@smithy/node-http-handler": ["@smithy/node-http-handler@4.5.0", "", { "dependencies": { "@smithy/abort-controller": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/querystring-builder": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Rnq9vQWiR1+/I6NZZMNzJHV6pZYyEHt2ZnuV3MG8z2NNenC4i/8Kzttz7CjZiHSmsN5frhXhg17z3Zqjjhmz1A=="], + + "@aws-sdk/client-sesv2/@smithy/smithy-client": ["@smithy/smithy-client@4.12.7", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-endpoint": "^4.4.27", "@smithy/middleware-stack": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-stream": "^4.5.20", "tslib": "^2.6.2" } }, "sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ=="], + + "@aws-sdk/client-sesv2/@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.43", "", { "dependencies": { "@smithy/property-provider": "^4.2.12", "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Qd/0wCKMaXxev/z00TvNzGCH2jlKKKxXP1aDxB6oKwSQthe3Og2dMhSayGCnsma1bK/kQX1+X7SMP99t6FgiiQ=="], + + "@aws-sdk/client-sesv2/@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.47", "", { "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.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-qSRbYp1EQ7th+sPFuVcVO05AE0QH635hycdEXlpzIahqHHf2Fyd/Zl+8v0XYMJ3cgDVPa0lkMefU7oNUjAP+DQ=="], + + "@aws-sdk/client-sesv2/@smithy/util-retry": ["@smithy/util-retry@4.2.12", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ=="], "@aws-sdk/client-sqs/@aws-sdk/core": ["@aws-sdk/core@3.947.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws-sdk/xml-builder": "3.930.0", "@smithy/core": "^3.18.7", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Khq4zHhuAkvCFuFbgcy3GrZTzfSX7ZIjIcW1zRDxXRLZKRtuhnZdonqTUfaWi5K42/4OmxkYNpsO7X7trQOeHw=="], "@aws-sdk/client-sqs/@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.947.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.947.0", "@aws-sdk/credential-provider-http": "3.947.0", "@aws-sdk/credential-provider-ini": "3.947.0", "@aws-sdk/credential-provider-process": "3.947.0", "@aws-sdk/credential-provider-sso": "3.947.0", "@aws-sdk/credential-provider-web-identity": "3.947.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-S0Zqebr71KyrT6J4uYPhwV65g4V5uDPHnd7dt2W34FcyPu+hVC7Hx4MFmsPyVLeT5cMCkkZvmY3kAoEzgUPJJg=="], + "@aws-sdk/client-sqs/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw=="], + + "@aws-sdk/client-sqs/@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw=="], + + "@aws-sdk/client-sqs/@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws/lambda-invoke-store": "^0.2.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-l4aGbHpXM45YNgXggIux1HgsCVAvvBoqHPkqLnqMl9QVapfuSTjJHfDYDsx1Xxct6/m7qSMUzanBALhiaGO2fA=="], + "@aws-sdk/client-sqs/@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.947.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@smithy/core": "^3.18.7", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-7rpKV8YNgCP2R4F9RjWZFcD2R+SO/0R4VHIbY9iZJdH2MzzJ8ZG7h8dZ2m8QkQd1fjx4wrFJGGPJUTYXPV3baA=="], + "@aws-sdk/client-sqs/@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/config-resolver": "^4.4.3", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw=="], + + "@aws-sdk/client-sqs/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="], + + "@aws-sdk/client-sqs/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-endpoints": "^3.2.5", "tslib": "^2.6.2" } }, "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w=="], + + "@aws-sdk/client-sqs/@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw=="], + "@aws-sdk/client-sqs/@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.947.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/types": "3.936.0", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-+vhHoDrdbb+zerV4noQk1DHaUMNzWFWPpPYjVTwW2186k5BEJIecAMChYkghRrBVJ3KPWP1+JnZwOd72F3d4rQ=="], - "@aws-sdk/middleware-bucket-endpoint/@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-sqs/@smithy/core": ["@smithy/core@3.23.12", "", { "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.20", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w=="], - "@aws-sdk/middleware-expect-continue/@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-sqs/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.27", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-serde": "^4.2.15", "@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-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA=="], - "@aws-sdk/middleware-flexible-checksums/@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-sqs/@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.44", "", { "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.7", "@smithy/types": "^4.13.1", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.12", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-Y1Rav7m5CFRPQyM4CI0koD/bXjyjJu3EQxZZhtLGD88WIrBrQ7kqXM96ncd6rYnojwOo/u9MXu57JrEvu/nLrA=="], + + "@aws-sdk/client-sqs/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.15", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg=="], + + "@aws-sdk/client-sqs/@smithy/node-http-handler": ["@smithy/node-http-handler@4.5.0", "", { "dependencies": { "@smithy/abort-controller": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/querystring-builder": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Rnq9vQWiR1+/I6NZZMNzJHV6pZYyEHt2ZnuV3MG8z2NNenC4i/8Kzttz7CjZiHSmsN5frhXhg17z3Zqjjhmz1A=="], + + "@aws-sdk/client-sqs/@smithy/smithy-client": ["@smithy/smithy-client@4.12.7", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-endpoint": "^4.4.27", "@smithy/middleware-stack": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-stream": "^4.5.20", "tslib": "^2.6.2" } }, "sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ=="], + + "@aws-sdk/client-sqs/@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.43", "", { "dependencies": { "@smithy/property-provider": "^4.2.12", "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Qd/0wCKMaXxev/z00TvNzGCH2jlKKKxXP1aDxB6oKwSQthe3Og2dMhSayGCnsma1bK/kQX1+X7SMP99t6FgiiQ=="], + + "@aws-sdk/client-sqs/@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.47", "", { "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.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-qSRbYp1EQ7th+sPFuVcVO05AE0QH635hycdEXlpzIahqHHf2Fyd/Zl+8v0XYMJ3cgDVPa0lkMefU7oNUjAP+DQ=="], + + "@aws-sdk/client-sqs/@smithy/util-retry": ["@smithy/util-retry@4.2.12", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ=="], + + "@aws-sdk/client-sso/@aws-sdk/core": ["@aws-sdk/core@3.940.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws-sdk/xml-builder": "3.930.0", "@smithy/core": "^3.18.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KsGD2FLaX5ngJao1mHxodIVU9VYd1E8810fcYiGwO1PFHDzf5BEkp6D9IdMeQwT8Q6JLYtiiT1Y/o3UCScnGoA=="], + + "@aws-sdk/client-sso/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw=="], + + "@aws-sdk/client-sso/@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw=="], + + "@aws-sdk/client-sso/@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws/lambda-invoke-store": "^0.2.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-l4aGbHpXM45YNgXggIux1HgsCVAvvBoqHPkqLnqMl9QVapfuSTjJHfDYDsx1Xxct6/m7qSMUzanBALhiaGO2fA=="], + + "@aws-sdk/client-sso/@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@smithy/core": "^3.18.5", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-nJbLrUj6fY+l2W2rIB9P4Qvpiy0tnTdg/dmixRxrU1z3e8wBdspJlyE+AZN4fuVbeL6rrRrO/zxQC1bB3cw5IA=="], + + "@aws-sdk/client-sso/@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/config-resolver": "^4.4.3", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw=="], + + "@aws-sdk/client-sso/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="], - "@aws-sdk/middleware-flexible-checksums/@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-sso/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-endpoints": "^3.2.5", "tslib": "^2.6.2" } }, "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w=="], - "@aws-sdk/middleware-location-constraint/@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-sso/@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw=="], + + "@aws-sdk/client-sso/@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.940.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-dlD/F+L/jN26I8Zg5x0oDGJiA+/WEQmnSE27fi5ydvYnpfQLwThtQo9SsNS47XSR/SOULaaoC9qx929rZuo74A=="], + + "@aws-sdk/client-sso/@smithy/core": ["@smithy/core@3.23.12", "", { "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.20", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w=="], + + "@aws-sdk/client-sso/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.27", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-serde": "^4.2.15", "@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-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA=="], + + "@aws-sdk/client-sso/@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.44", "", { "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.7", "@smithy/types": "^4.13.1", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.12", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-Y1Rav7m5CFRPQyM4CI0koD/bXjyjJu3EQxZZhtLGD88WIrBrQ7kqXM96ncd6rYnojwOo/u9MXu57JrEvu/nLrA=="], + + "@aws-sdk/client-sso/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.15", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg=="], + + "@aws-sdk/client-sso/@smithy/node-http-handler": ["@smithy/node-http-handler@4.5.0", "", { "dependencies": { "@smithy/abort-controller": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/querystring-builder": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Rnq9vQWiR1+/I6NZZMNzJHV6pZYyEHt2ZnuV3MG8z2NNenC4i/8Kzttz7CjZiHSmsN5frhXhg17z3Zqjjhmz1A=="], + + "@aws-sdk/client-sso/@smithy/smithy-client": ["@smithy/smithy-client@4.12.7", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-endpoint": "^4.4.27", "@smithy/middleware-stack": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-stream": "^4.5.20", "tslib": "^2.6.2" } }, "sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ=="], + + "@aws-sdk/client-sso/@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.43", "", { "dependencies": { "@smithy/property-provider": "^4.2.12", "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Qd/0wCKMaXxev/z00TvNzGCH2jlKKKxXP1aDxB6oKwSQthe3Og2dMhSayGCnsma1bK/kQX1+X7SMP99t6FgiiQ=="], + + "@aws-sdk/client-sso/@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.47", "", { "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.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-qSRbYp1EQ7th+sPFuVcVO05AE0QH635hycdEXlpzIahqHHf2Fyd/Zl+8v0XYMJ3cgDVPa0lkMefU7oNUjAP+DQ=="], + + "@aws-sdk/client-sso/@smithy/util-retry": ["@smithy/util-retry@4.2.12", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ=="], + + "@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/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/credential-provider-login/@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/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/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/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/eventstream-handler-node/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="], + + "@aws-sdk/lib-dynamodb/@aws-sdk/core": ["@aws-sdk/core@3.940.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws-sdk/xml-builder": "3.930.0", "@smithy/core": "^3.18.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KsGD2FLaX5ngJao1mHxodIVU9VYd1E8810fcYiGwO1PFHDzf5BEkp6D9IdMeQwT8Q6JLYtiiT1Y/o3UCScnGoA=="], + + "@aws-sdk/lib-dynamodb/@smithy/core": ["@smithy/core@3.23.12", "", { "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.20", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w=="], + + "@aws-sdk/lib-dynamodb/@smithy/smithy-client": ["@smithy/smithy-client@4.12.7", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-endpoint": "^4.4.27", "@smithy/middleware-stack": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-stream": "^4.5.20", "tslib": "^2.6.2" } }, "sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ=="], + + "@aws-sdk/middleware-endpoint-discovery/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="], + + "@aws-sdk/middleware-eventstream/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="], + + "@aws-sdk/middleware-flexible-checksums/@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/middleware-sdk-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/middleware-sdk-s3/@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/middleware-sdk-s3/@smithy/core": ["@smithy/core@3.23.12", "", { "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.20", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w=="], - "@aws-sdk/middleware-ssec/@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/middleware-sdk-s3/@smithy/smithy-client": ["@smithy/smithy-client@4.12.7", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-endpoint": "^4.4.27", "@smithy/middleware-stack": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-stream": "^4.5.20", "tslib": "^2.6.2" } }, "sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ=="], + + "@aws-sdk/middleware-sdk-sqs/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="], + + "@aws-sdk/middleware-sdk-sqs/@smithy/smithy-client": ["@smithy/smithy-client@4.12.7", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-endpoint": "^4.4.27", "@smithy/middleware-stack": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-stream": "^4.5.20", "tslib": "^2.6.2" } }, "sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ=="], + + "@aws-sdk/middleware-websocket/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="], "@aws-sdk/middleware-websocket/@aws-sdk/util-format-url": ["@aws-sdk/util-format-url@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/querystring-builder": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-MS5eSEtDUFIAMHrJaMERiHAvDPdfxc/T869ZjDNFAIiZhyc037REw0aoTNeimNXDNy2txRNZJaAUn/kE4RwN+g=="], - "@aws-sdk/s3-request-presigner/@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/nested-clients/@aws-sdk/core": ["@aws-sdk/core@3.940.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws-sdk/xml-builder": "3.930.0", "@smithy/core": "^3.18.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KsGD2FLaX5ngJao1mHxodIVU9VYd1E8810fcYiGwO1PFHDzf5BEkp6D9IdMeQwT8Q6JLYtiiT1Y/o3UCScnGoA=="], + + "@aws-sdk/nested-clients/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw=="], + + "@aws-sdk/nested-clients/@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw=="], + + "@aws-sdk/nested-clients/@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws/lambda-invoke-store": "^0.2.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-l4aGbHpXM45YNgXggIux1HgsCVAvvBoqHPkqLnqMl9QVapfuSTjJHfDYDsx1Xxct6/m7qSMUzanBALhiaGO2fA=="], + + "@aws-sdk/nested-clients/@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@smithy/core": "^3.18.5", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-nJbLrUj6fY+l2W2rIB9P4Qvpiy0tnTdg/dmixRxrU1z3e8wBdspJlyE+AZN4fuVbeL6rrRrO/zxQC1bB3cw5IA=="], + + "@aws-sdk/nested-clients/@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/config-resolver": "^4.4.3", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw=="], - "@aws-sdk/signature-v4-multi-region/@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/nested-clients/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="], - "@aws-sdk/util-format-url/@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/nested-clients/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-endpoints": "^3.2.5", "tslib": "^2.6.2" } }, "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w=="], - "@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], + "@aws-sdk/nested-clients/@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw=="], + + "@aws-sdk/nested-clients/@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.940.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-dlD/F+L/jN26I8Zg5x0oDGJiA+/WEQmnSE27fi5ydvYnpfQLwThtQo9SsNS47XSR/SOULaaoC9qx929rZuo74A=="], + + "@aws-sdk/nested-clients/@smithy/core": ["@smithy/core@3.23.12", "", { "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.20", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w=="], + + "@aws-sdk/nested-clients/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.27", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-serde": "^4.2.15", "@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-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA=="], + + "@aws-sdk/nested-clients/@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.44", "", { "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.7", "@smithy/types": "^4.13.1", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.12", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-Y1Rav7m5CFRPQyM4CI0koD/bXjyjJu3EQxZZhtLGD88WIrBrQ7kqXM96ncd6rYnojwOo/u9MXu57JrEvu/nLrA=="], + + "@aws-sdk/nested-clients/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.15", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg=="], + + "@aws-sdk/nested-clients/@smithy/node-http-handler": ["@smithy/node-http-handler@4.5.0", "", { "dependencies": { "@smithy/abort-controller": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/querystring-builder": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Rnq9vQWiR1+/I6NZZMNzJHV6pZYyEHt2ZnuV3MG8z2NNenC4i/8Kzttz7CjZiHSmsN5frhXhg17z3Zqjjhmz1A=="], + + "@aws-sdk/nested-clients/@smithy/smithy-client": ["@smithy/smithy-client@4.12.7", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-endpoint": "^4.4.27", "@smithy/middleware-stack": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-stream": "^4.5.20", "tslib": "^2.6.2" } }, "sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ=="], + + "@aws-sdk/nested-clients/@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.43", "", { "dependencies": { "@smithy/property-provider": "^4.2.12", "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Qd/0wCKMaXxev/z00TvNzGCH2jlKKKxXP1aDxB6oKwSQthe3Og2dMhSayGCnsma1bK/kQX1+X7SMP99t6FgiiQ=="], + + "@aws-sdk/nested-clients/@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.47", "", { "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.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-qSRbYp1EQ7th+sPFuVcVO05AE0QH635hycdEXlpzIahqHHf2Fyd/Zl+8v0XYMJ3cgDVPa0lkMefU7oNUjAP+DQ=="], + + "@aws-sdk/nested-clients/@smithy/util-retry": ["@smithy/util-retry@4.2.12", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ=="], + + "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.27", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-serde": "^4.2.15", "@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-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA=="], + + "@aws-sdk/s3-request-presigner/@smithy/smithy-client": ["@smithy/smithy-client@4.12.7", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-endpoint": "^4.4.27", "@smithy/middleware-stack": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-stream": "^4.5.20", "tslib": "^2.6.2" } }, "sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ=="], + + "@aws-sdk/token-providers/@aws-sdk/core": ["@aws-sdk/core@3.940.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws-sdk/xml-builder": "3.930.0", "@smithy/core": "^3.18.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KsGD2FLaX5ngJao1mHxodIVU9VYd1E8810fcYiGwO1PFHDzf5BEkp6D9IdMeQwT8Q6JLYtiiT1Y/o3UCScnGoA=="], + + "@aws-sdk/token-providers/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="], + + "@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=="], "@azure/communication-email/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="], @@ -4169,10 +4474,14 @@ "@shuding/opentype.js/fflate": ["fflate@0.7.4", "", {}, "sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw=="], - "@smithy/middleware-compression/@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=="], + "@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=="], "@smithy/middleware-compression/fflate": ["fflate@0.8.1", "", {}, "sha512-/exOvEuc+/iaUm105QIiOt4LpBdMTWsXxqR0HDF35vx3fmaKzw7354gTilCh5rkzEt8WYyG//ku3h3nRmd7CHQ=="], + "@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=="], + + "@smithy/util-stream/@smithy/node-http-handler": ["@smithy/node-http-handler@4.5.0", "", { "dependencies": { "@smithy/abort-controller": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/querystring-builder": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Rnq9vQWiR1+/I6NZZMNzJHV6pZYyEHt2ZnuV3MG8z2NNenC4i/8Kzttz7CjZiHSmsN5frhXhg17z3Zqjjhmz1A=="], + "@socket.io/redis-adapter/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], "@tailwindcss/node/jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], @@ -4569,23 +4878,75 @@ "@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-bedrock-runtime/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.930.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA=="], + + "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-/G3l5/wbZYP2XEQiOoIkRJmlv15f1P3MSd1a0gz27lHEMrOJOGq66rF1Ca4OJLzapWt3Fy9BPrZAepoAX11kMw=="], + + "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/node-http-handler": "^4.4.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/util-stream": "^4.5.6", "tslib": "^2.6.2" } }, "sha512-dOrc03DHElNBD6N9Okt4U0zhrG4Wix5QUBSZPr5VN8SvmjD9dkrrxOkkJaMCl/bzrW7kbQEp7LuBdbxArMmOZQ=="], + + "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/credential-provider-env": "3.940.0", "@aws-sdk/credential-provider-http": "3.940.0", "@aws-sdk/credential-provider-login": "3.940.0", "@aws-sdk/credential-provider-process": "3.940.0", "@aws-sdk/credential-provider-sso": "3.940.0", "@aws-sdk/credential-provider-web-identity": "3.940.0", "@aws-sdk/nested-clients": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-gn7PJQEzb/cnInNFTOaDoCN/hOKqMejNmLof1W5VW95Qk0TPO52lH8R4RmJPnRrwFMswOWswTOpR1roKNLIrcw=="], + + "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-pILBzt5/TYCqRsJb7vZlxmRIe0/T+FZPeml417EK75060ajDGnVJjHcuVdLVIeKoTKm9gmJc9l45gon6PbHyUQ=="], + + "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.940.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.940.0", "@aws-sdk/core": "3.940.0", "@aws-sdk/token-providers": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-q6JMHIkBlDCOMnA3RAzf8cGfup+8ukhhb50fNpghMs1SNBGhanmaMbZSgLigBRsPQW7fOk2l8jnzdVLS+BB9Uw=="], + + "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/nested-clients": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-9QLTIkDJHHaYL0nyymO41H8g3ui1yz6Y3GmAN1gYQa6plXisuFBnGAbmKVj7zNvjWaOKdF0dV3dd3AFKEDoJ/w=="], + + "@aws-sdk/client-cloudwatch-logs/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.930.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA=="], + + "@aws-sdk/client-cloudwatch-logs/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-/G3l5/wbZYP2XEQiOoIkRJmlv15f1P3MSd1a0gz27lHEMrOJOGq66rF1Ca4OJLzapWt3Fy9BPrZAepoAX11kMw=="], + + "@aws-sdk/client-cloudwatch-logs/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/node-http-handler": "^4.4.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/util-stream": "^4.5.6", "tslib": "^2.6.2" } }, "sha512-dOrc03DHElNBD6N9Okt4U0zhrG4Wix5QUBSZPr5VN8SvmjD9dkrrxOkkJaMCl/bzrW7kbQEp7LuBdbxArMmOZQ=="], + + "@aws-sdk/client-cloudwatch-logs/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/credential-provider-env": "3.940.0", "@aws-sdk/credential-provider-http": "3.940.0", "@aws-sdk/credential-provider-login": "3.940.0", "@aws-sdk/credential-provider-process": "3.940.0", "@aws-sdk/credential-provider-sso": "3.940.0", "@aws-sdk/credential-provider-web-identity": "3.940.0", "@aws-sdk/nested-clients": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-gn7PJQEzb/cnInNFTOaDoCN/hOKqMejNmLof1W5VW95Qk0TPO52lH8R4RmJPnRrwFMswOWswTOpR1roKNLIrcw=="], + + "@aws-sdk/client-cloudwatch-logs/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-pILBzt5/TYCqRsJb7vZlxmRIe0/T+FZPeml417EK75060ajDGnVJjHcuVdLVIeKoTKm9gmJc9l45gon6PbHyUQ=="], + + "@aws-sdk/client-cloudwatch-logs/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.940.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.940.0", "@aws-sdk/core": "3.940.0", "@aws-sdk/token-providers": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-q6JMHIkBlDCOMnA3RAzf8cGfup+8ukhhb50fNpghMs1SNBGhanmaMbZSgLigBRsPQW7fOk2l8jnzdVLS+BB9Uw=="], + + "@aws-sdk/client-cloudwatch-logs/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/nested-clients": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-9QLTIkDJHHaYL0nyymO41H8g3ui1yz6Y3GmAN1gYQa6plXisuFBnGAbmKVj7zNvjWaOKdF0dV3dd3AFKEDoJ/w=="], + + "@aws-sdk/client-cloudwatch/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.930.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA=="], - "@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-cloudwatch/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-/G3l5/wbZYP2XEQiOoIkRJmlv15f1P3MSd1a0gz27lHEMrOJOGq66rF1Ca4OJLzapWt3Fy9BPrZAepoAX11kMw=="], - "@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-cloudwatch/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/node-http-handler": "^4.4.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/util-stream": "^4.5.6", "tslib": "^2.6.2" } }, "sha512-dOrc03DHElNBD6N9Okt4U0zhrG4Wix5QUBSZPr5VN8SvmjD9dkrrxOkkJaMCl/bzrW7kbQEp7LuBdbxArMmOZQ=="], - "@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-cloudwatch/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/credential-provider-env": "3.940.0", "@aws-sdk/credential-provider-http": "3.940.0", "@aws-sdk/credential-provider-login": "3.940.0", "@aws-sdk/credential-provider-process": "3.940.0", "@aws-sdk/credential-provider-sso": "3.940.0", "@aws-sdk/credential-provider-web-identity": "3.940.0", "@aws-sdk/nested-clients": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-gn7PJQEzb/cnInNFTOaDoCN/hOKqMejNmLof1W5VW95Qk0TPO52lH8R4RmJPnRrwFMswOWswTOpR1roKNLIrcw=="], - "@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-cloudwatch/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-pILBzt5/TYCqRsJb7vZlxmRIe0/T+FZPeml417EK75060ajDGnVJjHcuVdLVIeKoTKm9gmJc9l45gon6PbHyUQ=="], - "@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-cloudwatch/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.940.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.940.0", "@aws-sdk/core": "3.940.0", "@aws-sdk/token-providers": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-q6JMHIkBlDCOMnA3RAzf8cGfup+8ukhhb50fNpghMs1SNBGhanmaMbZSgLigBRsPQW7fOk2l8jnzdVLS+BB9Uw=="], - "@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-cloudwatch/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/nested-clients": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-9QLTIkDJHHaYL0nyymO41H8g3ui1yz6Y3GmAN1gYQa6plXisuFBnGAbmKVj7zNvjWaOKdF0dV3dd3AFKEDoJ/w=="], - "@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-dynamodb/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.930.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA=="], - "@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-dynamodb/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-/G3l5/wbZYP2XEQiOoIkRJmlv15f1P3MSd1a0gz27lHEMrOJOGq66rF1Ca4OJLzapWt3Fy9BPrZAepoAX11kMw=="], + + "@aws-sdk/client-dynamodb/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/node-http-handler": "^4.4.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/util-stream": "^4.5.6", "tslib": "^2.6.2" } }, "sha512-dOrc03DHElNBD6N9Okt4U0zhrG4Wix5QUBSZPr5VN8SvmjD9dkrrxOkkJaMCl/bzrW7kbQEp7LuBdbxArMmOZQ=="], + + "@aws-sdk/client-dynamodb/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/credential-provider-env": "3.940.0", "@aws-sdk/credential-provider-http": "3.940.0", "@aws-sdk/credential-provider-login": "3.940.0", "@aws-sdk/credential-provider-process": "3.940.0", "@aws-sdk/credential-provider-sso": "3.940.0", "@aws-sdk/credential-provider-web-identity": "3.940.0", "@aws-sdk/nested-clients": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-gn7PJQEzb/cnInNFTOaDoCN/hOKqMejNmLof1W5VW95Qk0TPO52lH8R4RmJPnRrwFMswOWswTOpR1roKNLIrcw=="], + + "@aws-sdk/client-dynamodb/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-pILBzt5/TYCqRsJb7vZlxmRIe0/T+FZPeml417EK75060ajDGnVJjHcuVdLVIeKoTKm9gmJc9l45gon6PbHyUQ=="], + + "@aws-sdk/client-dynamodb/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.940.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.940.0", "@aws-sdk/core": "3.940.0", "@aws-sdk/token-providers": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-q6JMHIkBlDCOMnA3RAzf8cGfup+8ukhhb50fNpghMs1SNBGhanmaMbZSgLigBRsPQW7fOk2l8jnzdVLS+BB9Uw=="], + + "@aws-sdk/client-dynamodb/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/nested-clients": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-9QLTIkDJHHaYL0nyymO41H8g3ui1yz6Y3GmAN1gYQa6plXisuFBnGAbmKVj7zNvjWaOKdF0dV3dd3AFKEDoJ/w=="], + + "@aws-sdk/client-rds-data/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.930.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA=="], + + "@aws-sdk/client-rds-data/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-/G3l5/wbZYP2XEQiOoIkRJmlv15f1P3MSd1a0gz27lHEMrOJOGq66rF1Ca4OJLzapWt3Fy9BPrZAepoAX11kMw=="], + + "@aws-sdk/client-rds-data/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/node-http-handler": "^4.4.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/util-stream": "^4.5.6", "tslib": "^2.6.2" } }, "sha512-dOrc03DHElNBD6N9Okt4U0zhrG4Wix5QUBSZPr5VN8SvmjD9dkrrxOkkJaMCl/bzrW7kbQEp7LuBdbxArMmOZQ=="], + + "@aws-sdk/client-rds-data/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/credential-provider-env": "3.940.0", "@aws-sdk/credential-provider-http": "3.940.0", "@aws-sdk/credential-provider-login": "3.940.0", "@aws-sdk/credential-provider-process": "3.940.0", "@aws-sdk/credential-provider-sso": "3.940.0", "@aws-sdk/credential-provider-web-identity": "3.940.0", "@aws-sdk/nested-clients": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-gn7PJQEzb/cnInNFTOaDoCN/hOKqMejNmLof1W5VW95Qk0TPO52lH8R4RmJPnRrwFMswOWswTOpR1roKNLIrcw=="], + + "@aws-sdk/client-rds-data/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-pILBzt5/TYCqRsJb7vZlxmRIe0/T+FZPeml417EK75060ajDGnVJjHcuVdLVIeKoTKm9gmJc9l45gon6PbHyUQ=="], + + "@aws-sdk/client-rds-data/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.940.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.940.0", "@aws-sdk/core": "3.940.0", "@aws-sdk/token-providers": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-q6JMHIkBlDCOMnA3RAzf8cGfup+8ukhhb50fNpghMs1SNBGhanmaMbZSgLigBRsPQW7fOk2l8jnzdVLS+BB9Uw=="], + + "@aws-sdk/client-rds-data/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/nested-clients": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-9QLTIkDJHHaYL0nyymO41H8g3ui1yz6Y3GmAN1gYQa6plXisuFBnGAbmKVj7zNvjWaOKdF0dV3dd3AFKEDoJ/w=="], "@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=="], @@ -4601,9 +4962,25 @@ "@aws-sdk/client-s3/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@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/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-J6H4R1nvr3uBTqD/EeIPAskrBtET4WFfNhpFySr2xW7bVZOXpQfPjrLSIx65jcNjBmLXzWq8QFLdVoGxiGG/SA=="], - "@aws-sdk/client-secrets-manager/@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-secrets-manager/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.930.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA=="], + + "@aws-sdk/client-secrets-manager/@aws-sdk/core/@smithy/core": ["@smithy/core@3.23.12", "", { "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.20", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w=="], + + "@aws-sdk/client-secrets-manager/@aws-sdk/core/@smithy/smithy-client": ["@smithy/smithy-client@4.12.7", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-endpoint": "^4.4.27", "@smithy/middleware-stack": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-stream": "^4.5.20", "tslib": "^2.6.2" } }, "sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ=="], + + "@aws-sdk/client-secrets-manager/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-/G3l5/wbZYP2XEQiOoIkRJmlv15f1P3MSd1a0gz27lHEMrOJOGq66rF1Ca4OJLzapWt3Fy9BPrZAepoAX11kMw=="], + + "@aws-sdk/client-secrets-manager/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/node-http-handler": "^4.4.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/util-stream": "^4.5.6", "tslib": "^2.6.2" } }, "sha512-dOrc03DHElNBD6N9Okt4U0zhrG4Wix5QUBSZPr5VN8SvmjD9dkrrxOkkJaMCl/bzrW7kbQEp7LuBdbxArMmOZQ=="], - "@aws-sdk/client-secrets-manager/@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-secrets-manager/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/credential-provider-env": "3.940.0", "@aws-sdk/credential-provider-http": "3.940.0", "@aws-sdk/credential-provider-login": "3.940.0", "@aws-sdk/credential-provider-process": "3.940.0", "@aws-sdk/credential-provider-sso": "3.940.0", "@aws-sdk/credential-provider-web-identity": "3.940.0", "@aws-sdk/nested-clients": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-gn7PJQEzb/cnInNFTOaDoCN/hOKqMejNmLof1W5VW95Qk0TPO52lH8R4RmJPnRrwFMswOWswTOpR1roKNLIrcw=="], + + "@aws-sdk/client-secrets-manager/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-pILBzt5/TYCqRsJb7vZlxmRIe0/T+FZPeml417EK75060ajDGnVJjHcuVdLVIeKoTKm9gmJc9l45gon6PbHyUQ=="], + + "@aws-sdk/client-secrets-manager/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.940.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.940.0", "@aws-sdk/core": "3.940.0", "@aws-sdk/token-providers": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-q6JMHIkBlDCOMnA3RAzf8cGfup+8ukhhb50fNpghMs1SNBGhanmaMbZSgLigBRsPQW7fOk2l8jnzdVLS+BB9Uw=="], + + "@aws-sdk/client-secrets-manager/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/nested-clients": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-9QLTIkDJHHaYL0nyymO41H8g3ui1yz6Y3GmAN1gYQa6plXisuFBnGAbmKVj7zNvjWaOKdF0dV3dd3AFKEDoJ/w=="], + + "@aws-sdk/client-secrets-manager/@aws-sdk/middleware-user-agent/@smithy/core": ["@smithy/core@3.23.12", "", { "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.20", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w=="], "@aws-sdk/client-sesv2/@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=="], @@ -4619,6 +4996,8 @@ "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@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/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-J6H4R1nvr3uBTqD/EeIPAskrBtET4WFfNhpFySr2xW7bVZOXpQfPjrLSIx65jcNjBmLXzWq8QFLdVoGxiGG/SA=="], + "@aws-sdk/client-sqs/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.930.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA=="], + "@aws-sdk/client-sqs/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.947.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-VR2V6dRELmzwAsCpK4GqxUi6UW5WNhAXS9F9AzWi5jvijwJo3nH92YNJUP4quMpgFZxJHEWyXLWgPjh9u0zYOA=="], "@aws-sdk/client-sqs/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.947.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/types": "3.936.0", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/node-http-handler": "^4.4.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/util-stream": "^4.5.6", "tslib": "^2.6.2" } }, "sha512-inF09lh9SlHj63Vmr5d+LmwPXZc2IbK8lAruhOr3KLsZAIHEgHgGPXWDC2ukTEMzg0pkexQ6FOhXXad6klK4RA=="], @@ -4631,10 +5010,42 @@ "@aws-sdk/client-sqs/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.947.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/nested-clients": "3.947.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-gokm/e/YHiHLrZgLq4j8tNAn8RJDPbIcglFRKgy08q8DmAqHQ8MXAKW3eS0QjAuRXU9mcMmUo1NrX6FRNBCCPw=="], + "@aws-sdk/client-sso/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.930.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA=="], + + "@aws-sdk/lib-dynamodb/@aws-sdk/core/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="], + + "@aws-sdk/lib-dynamodb/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.930.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA=="], + + "@aws-sdk/lib-dynamodb/@smithy/smithy-client/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.27", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-serde": "^4.2.15", "@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-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA=="], + "@aws-sdk/middleware-flexible-checksums/@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/middleware-flexible-checksums/@aws-sdk/core/@smithy/core": ["@smithy/core@3.23.12", "", { "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.20", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w=="], + + "@aws-sdk/middleware-flexible-checksums/@aws-sdk/core/@smithy/smithy-client": ["@smithy/smithy-client@4.12.7", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-endpoint": "^4.4.27", "@smithy/middleware-stack": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-stream": "^4.5.20", "tslib": "^2.6.2" } }, "sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ=="], + "@aws-sdk/middleware-sdk-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/middleware-sdk-s3/@smithy/smithy-client/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.27", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-serde": "^4.2.15", "@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-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA=="], + + "@aws-sdk/middleware-sdk-sqs/@smithy/smithy-client/@smithy/core": ["@smithy/core@3.23.12", "", { "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.20", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w=="], + + "@aws-sdk/middleware-sdk-sqs/@smithy/smithy-client/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.27", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-serde": "^4.2.15", "@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-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA=="], + + "@aws-sdk/nested-clients/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.930.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA=="], + + "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core": ["@smithy/core@3.23.12", "", { "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.20", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w=="], + + "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.15", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg=="], + + "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/core": ["@smithy/core@3.23.12", "", { "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.20", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w=="], + + "@aws-sdk/token-providers/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.930.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA=="], + + "@aws-sdk/token-providers/@aws-sdk/core/@smithy/core": ["@smithy/core@3.23.12", "", { "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.20", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w=="], + + "@aws-sdk/token-providers/@aws-sdk/core/@smithy/smithy-client": ["@smithy/smithy-client@4.12.7", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-endpoint": "^4.4.27", "@smithy/middleware-stack": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-stream": "^4.5.20", "tslib": "^2.6.2" } }, "sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ=="], + "@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], "@browserbasehq/sdk/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], @@ -4725,8 +5136,6 @@ "@shikijs/rehype/shiki/@shikijs/themes": ["@shikijs/themes@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA=="], - "@smithy/middleware-compression/@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=="], - "@trigger.dev/core/@opentelemetry/core/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.40.0", "", {}, "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw=="], "@trigger.dev/core/@opentelemetry/exporter-logs-otlp-http/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.203.0", "", { "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/otlp-transformer": "0.203.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-Wbxf7k+87KyvxFr5D7uOiSq/vHXWommvdnNE7vECO3tAhsA2GfOlpWINCMWUEPdHZ7tCXxw6Epp3vgx3jU7llQ=="], @@ -5143,19 +5552,25 @@ "@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-bedrock-runtime/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], + + "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/nested-clients": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-fOKC3VZkwa9T2l2VFKWRtfHQPQuISqqNl35ZhcXjWKVwRwl/o7THPMkqI4XwgT2noGa7LLYVbWMwnsgSsBqglg=="], + + "@aws-sdk/client-cloudwatch-logs/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], - "@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-cloudwatch-logs/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/nested-clients": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-fOKC3VZkwa9T2l2VFKWRtfHQPQuISqqNl35ZhcXjWKVwRwl/o7THPMkqI4XwgT2noGa7LLYVbWMwnsgSsBqglg=="], - "@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-cloudwatch/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], - "@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-cloudwatch/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/nested-clients": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-fOKC3VZkwa9T2l2VFKWRtfHQPQuISqqNl35ZhcXjWKVwRwl/o7THPMkqI4XwgT2noGa7LLYVbWMwnsgSsBqglg=="], - "@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-dynamodb/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], - "@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-dynamodb/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/nested-clients": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-fOKC3VZkwa9T2l2VFKWRtfHQPQuISqqNl35ZhcXjWKVwRwl/o7THPMkqI4XwgT2noGa7LLYVbWMwnsgSsBqglg=="], - "@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-rds-data/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], + + "@aws-sdk/client-rds-data/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/nested-clients": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-fOKC3VZkwa9T2l2VFKWRtfHQPQuISqqNl35ZhcXjWKVwRwl/o7THPMkqI4XwgT2noGa7LLYVbWMwnsgSsBqglg=="], "@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=="], @@ -5169,6 +5584,16 @@ "@aws-sdk/client-s3/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.996.14", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.24", "@aws-sdk/middleware-host-header": "^3.972.8", "@aws-sdk/middleware-logger": "^3.972.8", "@aws-sdk/middleware-recursion-detection": "^3.972.8", "@aws-sdk/middleware-user-agent": "^3.972.25", "@aws-sdk/region-config-resolver": "^3.972.9", "@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.11", "@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", "tslib": "^2.6.2" } }, "sha512-fSESKvh1VbfjtV3QMnRkCPZWkUbQof6T/DOpiLp33yP2wA+rbwwnZeG3XT3Ekljgw2I8X4XaQPnw+zSR8yxJ5Q=="], + "@aws-sdk/client-secrets-manager/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], + + "@aws-sdk/client-secrets-manager/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.27", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-serde": "^4.2.15", "@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-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA=="], + + "@aws-sdk/client-secrets-manager/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/node-http-handler": ["@smithy/node-http-handler@4.5.0", "", { "dependencies": { "@smithy/abort-controller": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/querystring-builder": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Rnq9vQWiR1+/I6NZZMNzJHV6pZYyEHt2ZnuV3MG8z2NNenC4i/8Kzttz7CjZiHSmsN5frhXhg17z3Zqjjhmz1A=="], + + "@aws-sdk/client-secrets-manager/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/smithy-client": ["@smithy/smithy-client@4.12.7", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-endpoint": "^4.4.27", "@smithy/middleware-stack": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-stream": "^4.5.20", "tslib": "^2.6.2" } }, "sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ=="], + + "@aws-sdk/client-secrets-manager/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.940.0", "", { "dependencies": { "@aws-sdk/core": "3.940.0", "@aws-sdk/nested-clients": "3.940.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-fOKC3VZkwa9T2l2VFKWRtfHQPQuISqqNl35ZhcXjWKVwRwl/o7THPMkqI4XwgT2noGa7LLYVbWMwnsgSsBqglg=="], + "@aws-sdk/client-sesv2/@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-sesv2/@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=="], @@ -5181,6 +5606,8 @@ "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.996.14", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.24", "@aws-sdk/middleware-host-header": "^3.972.8", "@aws-sdk/middleware-logger": "^3.972.8", "@aws-sdk/middleware-recursion-detection": "^3.972.8", "@aws-sdk/middleware-user-agent": "^3.972.25", "@aws-sdk/region-config-resolver": "^3.972.9", "@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.11", "@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", "tslib": "^2.6.2" } }, "sha512-fSESKvh1VbfjtV3QMnRkCPZWkUbQof6T/DOpiLp33yP2wA+rbwwnZeG3XT3Ekljgw2I8X4XaQPnw+zSR8yxJ5Q=="], + "@aws-sdk/client-sqs/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], + "@aws-sdk/client-sqs/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.947.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/nested-clients": "3.947.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-u7M3hazcB7aJiVwosNdJRbIJDzbwQ861NTtl6S0HmvWpixaVb7iyhJZWg8/plyUznboZGBm7JVEdxtxv3u0bTA=="], "@aws-sdk/client-sqs/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.947.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.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.947.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.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@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.14", "@smithy/middleware-retry": "^4.4.14", "@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.10", "@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.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@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-DjRJEYNnHUTu9kGPPQDTSXquwSEd6myKR4ssI4FaYLFhdT3ldWpj73yYt807H3tdmhS7vPmdVqchSJnjurUQAw=="], @@ -5191,10 +5618,28 @@ "@aws-sdk/client-sqs/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.947.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.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.947.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.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@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.14", "@smithy/middleware-retry": "^4.4.14", "@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.10", "@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.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@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-DjRJEYNnHUTu9kGPPQDTSXquwSEd6myKR4ssI4FaYLFhdT3ldWpj73yYt807H3tdmhS7vPmdVqchSJnjurUQAw=="], + "@aws-sdk/client-sso/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], + + "@aws-sdk/lib-dynamodb/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], + + "@aws-sdk/lib-dynamodb/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.15", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg=="], + "@aws-sdk/middleware-flexible-checksums/@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/middleware-flexible-checksums/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.27", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-serde": "^4.2.15", "@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-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA=="], + "@aws-sdk/middleware-sdk-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/middleware-sdk-s3/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.15", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg=="], + + "@aws-sdk/middleware-sdk-sqs/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.15", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg=="], + + "@aws-sdk/nested-clients/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], + + "@aws-sdk/token-providers/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], + + "@aws-sdk/token-providers/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.27", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-serde": "^4.2.15", "@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-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA=="], + "@browserbasehq/sdk/node-fetch/whatwg-url/tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], "@browserbasehq/sdk/node-fetch/whatwg-url/webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], @@ -5207,8 +5652,6 @@ "@cerebras/cerebras_cloud_sdk/node-fetch/whatwg-url/webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], - "@smithy/middleware-compression/@smithy/core/@smithy/util-stream/@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=="], - "@trigger.dev/core/socket.io-client/engine.io-client/ws": ["ws@8.17.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ=="], "@trigger.dev/core/socket.io-client/engine.io-client/xmlhttprequest-ssl": ["xmlhttprequest-ssl@2.0.0", "", {}, "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A=="], @@ -5289,8 +5732,18 @@ "test-exclude/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + "@aws-sdk/client-secrets-manager/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.15", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg=="], + + "@aws-sdk/client-secrets-manager/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/smithy-client/@smithy/core": ["@smithy/core@3.23.12", "", { "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.20", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w=="], + + "@aws-sdk/client-secrets-manager/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/smithy-client/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.27", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-serde": "^4.2.15", "@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-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA=="], + "@aws-sdk/client-sqs/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.947.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.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.947.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.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@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.14", "@smithy/middleware-retry": "^4.4.14", "@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.10", "@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.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@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-DjRJEYNnHUTu9kGPPQDTSXquwSEd6myKR4ssI4FaYLFhdT3ldWpj73yYt807H3tdmhS7vPmdVqchSJnjurUQAw=="], + "@aws-sdk/middleware-flexible-checksums/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.15", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg=="], + + "@aws-sdk/token-providers/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.15", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg=="], + "@browserbasehq/stagehand/@anthropic-ai/sdk/node-fetch/whatwg-url/tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], "@browserbasehq/stagehand/@anthropic-ai/sdk/node-fetch/whatwg-url/webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], @@ -5323,6 +5776,8 @@ "test-exclude/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + "@aws-sdk/client-secrets-manager/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.15", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg=="], + "lint-staged/listr2/cli-truncate/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], "lint-staged/listr2/log-update/cli-cursor/restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], diff --git a/helm/sim/values.yaml b/helm/sim/values.yaml index b7b32cb4c4e..2e596c69bcf 100644 --- a/helm/sim/values.yaml +++ b/helm/sim/values.yaml @@ -985,6 +985,15 @@ cronjobs: successfulJobsHistoryLimit: 3 failedJobsHistoryLimit: 1 + connectorSync: + enabled: true + name: connector-sync + schedule: "*/5 * * * *" + path: "/api/knowledge/connectors/sync" + concurrencyPolicy: Forbid + successfulJobsHistoryLimit: 3 + failedJobsHistoryLimit: 1 + # Global CronJob settings image: repository: curlimages/curl diff --git a/package.json b/package.json index 9cd7279b997..c7ff404e8c2 100644 --- a/package.json +++ b/package.json @@ -48,5 +48,8 @@ }, "trustedDependencies": [ "sharp" - ] + ], + "dependencies": { + "@aws-sdk/client-athena": "3.1024.0" + } }