mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
Compare commits
2 Commits
improvemen
...
feat/fix-l
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
498504c35b | ||
|
|
c367da81fb |
96
apps/sim/app/api/tools/cloudwatch/describe-alarms/route.ts
Normal file
96
apps/sim/app/api/tools/cloudwatch/describe-alarms/route.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import {
|
||||
type AlarmType,
|
||||
CloudWatchClient,
|
||||
DescribeAlarmsCommand,
|
||||
type StateValue,
|
||||
} from '@aws-sdk/client-cloudwatch'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
|
||||
const logger = createLogger('CloudWatchDescribeAlarms')
|
||||
|
||||
const DescribeAlarmsSchema = 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'),
|
||||
alarmNamePrefix: z.string().optional(),
|
||||
stateValue: z.preprocess(
|
||||
(v) => (v === '' ? undefined : v),
|
||||
z.enum(['OK', 'ALARM', 'INSUFFICIENT_DATA']).optional()
|
||||
),
|
||||
alarmType: z.preprocess(
|
||||
(v) => (v === '' ? undefined : v),
|
||||
z.enum(['MetricAlarm', 'CompositeAlarm']).optional()
|
||||
),
|
||||
limit: z.preprocess(
|
||||
(v) => (v === '' || v === undefined || v === null ? undefined : v),
|
||||
z.number({ coerce: true }).int().positive().optional()
|
||||
),
|
||||
})
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
const body = await request.json()
|
||||
const validatedData = DescribeAlarmsSchema.parse(body)
|
||||
|
||||
const client = new CloudWatchClient({
|
||||
region: validatedData.region,
|
||||
credentials: {
|
||||
accessKeyId: validatedData.accessKeyId,
|
||||
secretAccessKey: validatedData.secretAccessKey,
|
||||
},
|
||||
})
|
||||
|
||||
const command = new DescribeAlarmsCommand({
|
||||
...(validatedData.alarmNamePrefix && { AlarmNamePrefix: validatedData.alarmNamePrefix }),
|
||||
...(validatedData.stateValue && { StateValue: validatedData.stateValue as StateValue }),
|
||||
...(validatedData.alarmType && { AlarmTypes: [validatedData.alarmType as AlarmType] }),
|
||||
...(validatedData.limit !== undefined && { MaxRecords: validatedData.limit }),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
|
||||
const metricAlarms = (response.MetricAlarms ?? []).map((a) => ({
|
||||
alarmName: a.AlarmName ?? '',
|
||||
alarmArn: a.AlarmArn ?? '',
|
||||
stateValue: a.StateValue ?? 'UNKNOWN',
|
||||
stateReason: a.StateReason ?? '',
|
||||
metricName: a.MetricName,
|
||||
namespace: a.Namespace,
|
||||
comparisonOperator: a.ComparisonOperator,
|
||||
threshold: a.Threshold,
|
||||
evaluationPeriods: a.EvaluationPeriods,
|
||||
stateUpdatedTimestamp: a.StateUpdatedTimestamp?.getTime(),
|
||||
}))
|
||||
|
||||
const compositeAlarms = (response.CompositeAlarms ?? []).map((a) => ({
|
||||
alarmName: a.AlarmName ?? '',
|
||||
alarmArn: a.AlarmArn ?? '',
|
||||
stateValue: a.StateValue ?? 'UNKNOWN',
|
||||
stateReason: a.StateReason ?? '',
|
||||
metricName: undefined,
|
||||
namespace: undefined,
|
||||
comparisonOperator: undefined,
|
||||
threshold: undefined,
|
||||
evaluationPeriods: undefined,
|
||||
stateUpdatedTimestamp: a.StateUpdatedTimestamp?.getTime(),
|
||||
}))
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
output: { alarms: [...metricAlarms, ...compositeAlarms] },
|
||||
})
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : 'Failed to describe CloudWatch alarms'
|
||||
logger.error('DescribeAlarms failed', { error: errorMessage })
|
||||
return NextResponse.json({ error: errorMessage }, { status: 500 })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import { DescribeLogGroupsCommand } from '@aws-sdk/client-cloudwatch-logs'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { createCloudWatchLogsClient } from '@/app/api/tools/cloudwatch/utils'
|
||||
|
||||
const logger = createLogger('CloudWatchDescribeLogGroups')
|
||||
|
||||
const DescribeLogGroupsSchema = 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'),
|
||||
prefix: z.string().optional(),
|
||||
limit: z.preprocess(
|
||||
(v) => (v === '' || v === undefined || v === null ? undefined : v),
|
||||
z.number({ coerce: true }).int().positive().optional()
|
||||
),
|
||||
})
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const auth = await checkSessionOrInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
const body = await request.json()
|
||||
const validatedData = DescribeLogGroupsSchema.parse(body)
|
||||
|
||||
const client = createCloudWatchLogsClient({
|
||||
region: validatedData.region,
|
||||
accessKeyId: validatedData.accessKeyId,
|
||||
secretAccessKey: validatedData.secretAccessKey,
|
||||
})
|
||||
|
||||
const command = new DescribeLogGroupsCommand({
|
||||
...(validatedData.prefix && { logGroupNamePrefix: validatedData.prefix }),
|
||||
...(validatedData.limit !== undefined && { limit: validatedData.limit }),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
|
||||
const logGroups = (response.logGroups ?? []).map((lg) => ({
|
||||
logGroupName: lg.logGroupName ?? '',
|
||||
arn: lg.arn ?? '',
|
||||
storedBytes: lg.storedBytes ?? 0,
|
||||
retentionInDays: lg.retentionInDays,
|
||||
creationTime: lg.creationTime,
|
||||
}))
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
output: { logGroups },
|
||||
})
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : 'Failed to describe CloudWatch log groups'
|
||||
logger.error('DescribeLogGroups failed', { error: errorMessage })
|
||||
return NextResponse.json({ error: errorMessage }, { status: 500 })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { createCloudWatchLogsClient, describeLogStreams } from '@/app/api/tools/cloudwatch/utils'
|
||||
|
||||
const logger = createLogger('CloudWatchDescribeLogStreams')
|
||||
|
||||
const DescribeLogStreamsSchema = 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'),
|
||||
logGroupName: z.string().min(1, 'Log group name is required'),
|
||||
prefix: z.string().optional(),
|
||||
limit: z.preprocess(
|
||||
(v) => (v === '' || v === undefined || v === null ? undefined : v),
|
||||
z.number({ coerce: true }).int().positive().optional()
|
||||
),
|
||||
})
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const auth = await checkSessionOrInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
const body = await request.json()
|
||||
const validatedData = DescribeLogStreamsSchema.parse(body)
|
||||
|
||||
const client = createCloudWatchLogsClient({
|
||||
region: validatedData.region,
|
||||
accessKeyId: validatedData.accessKeyId,
|
||||
secretAccessKey: validatedData.secretAccessKey,
|
||||
})
|
||||
|
||||
const result = await describeLogStreams(client, validatedData.logGroupName, {
|
||||
prefix: validatedData.prefix,
|
||||
limit: validatedData.limit,
|
||||
})
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
output: { logStreams: result.logStreams },
|
||||
})
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : 'Failed to describe CloudWatch log streams'
|
||||
logger.error('DescribeLogStreams failed', { error: errorMessage })
|
||||
return NextResponse.json({ error: errorMessage }, { status: 500 })
|
||||
}
|
||||
}
|
||||
60
apps/sim/app/api/tools/cloudwatch/get-log-events/route.ts
Normal file
60
apps/sim/app/api/tools/cloudwatch/get-log-events/route.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { createCloudWatchLogsClient, getLogEvents } from '@/app/api/tools/cloudwatch/utils'
|
||||
|
||||
const logger = createLogger('CloudWatchGetLogEvents')
|
||||
|
||||
const GetLogEventsSchema = 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'),
|
||||
logGroupName: z.string().min(1, 'Log group name is required'),
|
||||
logStreamName: z.string().min(1, 'Log stream name is required'),
|
||||
startTime: z.number({ coerce: true }).int().optional(),
|
||||
endTime: z.number({ coerce: true }).int().optional(),
|
||||
limit: z.preprocess(
|
||||
(v) => (v === '' || v === undefined || v === null ? undefined : v),
|
||||
z.number({ coerce: true }).int().positive().optional()
|
||||
),
|
||||
})
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
const body = await request.json()
|
||||
const validatedData = GetLogEventsSchema.parse(body)
|
||||
|
||||
const client = createCloudWatchLogsClient({
|
||||
region: validatedData.region,
|
||||
accessKeyId: validatedData.accessKeyId,
|
||||
secretAccessKey: validatedData.secretAccessKey,
|
||||
})
|
||||
|
||||
const result = await getLogEvents(
|
||||
client,
|
||||
validatedData.logGroupName,
|
||||
validatedData.logStreamName,
|
||||
{
|
||||
startTime: validatedData.startTime,
|
||||
endTime: validatedData.endTime,
|
||||
limit: validatedData.limit,
|
||||
}
|
||||
)
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
output: { events: result.events },
|
||||
})
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : 'Failed to get CloudWatch log events'
|
||||
logger.error('GetLogEvents failed', { error: errorMessage })
|
||||
return NextResponse.json({ error: errorMessage }, { status: 500 })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
import { CloudWatchClient, GetMetricStatisticsCommand } from '@aws-sdk/client-cloudwatch'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
|
||||
const logger = createLogger('CloudWatchGetMetricStatistics')
|
||||
|
||||
const GetMetricStatisticsSchema = 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'),
|
||||
namespace: z.string().min(1, 'Namespace is required'),
|
||||
metricName: z.string().min(1, 'Metric name is required'),
|
||||
startTime: z.number({ coerce: true }).int(),
|
||||
endTime: z.number({ coerce: true }).int(),
|
||||
period: z.number({ coerce: true }).int().min(1),
|
||||
statistics: z.array(z.enum(['Average', 'Sum', 'Minimum', 'Maximum', 'SampleCount'])).min(1),
|
||||
dimensions: z.string().optional(),
|
||||
})
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
const body = await request.json()
|
||||
const validatedData = GetMetricStatisticsSchema.parse(body)
|
||||
|
||||
const client = new CloudWatchClient({
|
||||
region: validatedData.region,
|
||||
credentials: {
|
||||
accessKeyId: validatedData.accessKeyId,
|
||||
secretAccessKey: validatedData.secretAccessKey,
|
||||
},
|
||||
})
|
||||
|
||||
let parsedDimensions: { Name: string; Value: string }[] | undefined
|
||||
if (validatedData.dimensions) {
|
||||
try {
|
||||
const dims = JSON.parse(validatedData.dimensions)
|
||||
if (Array.isArray(dims)) {
|
||||
parsedDimensions = dims.map((d: Record<string, string>) => ({
|
||||
Name: d.name,
|
||||
Value: d.value,
|
||||
}))
|
||||
} else if (typeof dims === 'object') {
|
||||
parsedDimensions = Object.entries(dims).map(([name, value]) => ({
|
||||
Name: name,
|
||||
Value: String(value),
|
||||
}))
|
||||
}
|
||||
} catch {
|
||||
throw new Error('Invalid dimensions JSON')
|
||||
}
|
||||
}
|
||||
|
||||
const command = new GetMetricStatisticsCommand({
|
||||
Namespace: validatedData.namespace,
|
||||
MetricName: validatedData.metricName,
|
||||
StartTime: new Date(validatedData.startTime * 1000),
|
||||
EndTime: new Date(validatedData.endTime * 1000),
|
||||
Period: validatedData.period,
|
||||
Statistics: validatedData.statistics,
|
||||
...(parsedDimensions && { Dimensions: parsedDimensions }),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
|
||||
const datapoints = (response.Datapoints ?? [])
|
||||
.sort((a, b) => (a.Timestamp?.getTime() ?? 0) - (b.Timestamp?.getTime() ?? 0))
|
||||
.map((dp) => ({
|
||||
timestamp: dp.Timestamp ? Math.floor(dp.Timestamp.getTime() / 1000) : 0,
|
||||
average: dp.Average,
|
||||
sum: dp.Sum,
|
||||
minimum: dp.Minimum,
|
||||
maximum: dp.Maximum,
|
||||
sampleCount: dp.SampleCount,
|
||||
unit: dp.Unit,
|
||||
}))
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
output: {
|
||||
label: response.Label ?? validatedData.metricName,
|
||||
datapoints,
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : 'Failed to get CloudWatch metric statistics'
|
||||
logger.error('GetMetricStatistics failed', { error: errorMessage })
|
||||
return NextResponse.json({ error: errorMessage }, { status: 500 })
|
||||
}
|
||||
}
|
||||
67
apps/sim/app/api/tools/cloudwatch/list-metrics/route.ts
Normal file
67
apps/sim/app/api/tools/cloudwatch/list-metrics/route.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { CloudWatchClient, ListMetricsCommand } from '@aws-sdk/client-cloudwatch'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
|
||||
const logger = createLogger('CloudWatchListMetrics')
|
||||
|
||||
const ListMetricsSchema = 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'),
|
||||
namespace: z.string().optional(),
|
||||
metricName: z.string().optional(),
|
||||
recentlyActive: z.boolean().optional(),
|
||||
limit: z.preprocess(
|
||||
(v) => (v === '' || v === undefined || v === null ? undefined : v),
|
||||
z.number({ coerce: true }).int().positive().optional()
|
||||
),
|
||||
})
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
const body = await request.json()
|
||||
const validatedData = ListMetricsSchema.parse(body)
|
||||
|
||||
const client = new CloudWatchClient({
|
||||
region: validatedData.region,
|
||||
credentials: {
|
||||
accessKeyId: validatedData.accessKeyId,
|
||||
secretAccessKey: validatedData.secretAccessKey,
|
||||
},
|
||||
})
|
||||
|
||||
const command = new ListMetricsCommand({
|
||||
...(validatedData.namespace && { Namespace: validatedData.namespace }),
|
||||
...(validatedData.metricName && { MetricName: validatedData.metricName }),
|
||||
...(validatedData.recentlyActive && { RecentlyActive: 'PT3H' }),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
|
||||
const metrics = (response.Metrics ?? []).slice(0, validatedData.limit ?? 500).map((m) => ({
|
||||
namespace: m.Namespace ?? '',
|
||||
metricName: m.MetricName ?? '',
|
||||
dimensions: (m.Dimensions ?? []).map((d) => ({
|
||||
name: d.Name ?? '',
|
||||
value: d.Value ?? '',
|
||||
})),
|
||||
}))
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
output: { metrics },
|
||||
})
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : 'Failed to list CloudWatch metrics'
|
||||
logger.error('ListMetrics failed', { error: errorMessage })
|
||||
return NextResponse.json({ error: errorMessage }, { status: 500 })
|
||||
}
|
||||
}
|
||||
71
apps/sim/app/api/tools/cloudwatch/query-logs/route.ts
Normal file
71
apps/sim/app/api/tools/cloudwatch/query-logs/route.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { StartQueryCommand } from '@aws-sdk/client-cloudwatch-logs'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { createCloudWatchLogsClient, pollQueryResults } from '@/app/api/tools/cloudwatch/utils'
|
||||
|
||||
const logger = createLogger('CloudWatchQueryLogs')
|
||||
|
||||
const QueryLogsSchema = 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'),
|
||||
logGroupNames: z.array(z.string().min(1)).min(1, 'At least one log group name is required'),
|
||||
queryString: z.string().min(1, 'Query string is required'),
|
||||
startTime: z.number({ coerce: true }).int(),
|
||||
endTime: z.number({ coerce: true }).int(),
|
||||
limit: z.preprocess(
|
||||
(v) => (v === '' || v === undefined || v === null ? undefined : v),
|
||||
z.number({ coerce: true }).int().positive().optional()
|
||||
),
|
||||
})
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
const body = await request.json()
|
||||
const validatedData = QueryLogsSchema.parse(body)
|
||||
|
||||
const client = createCloudWatchLogsClient({
|
||||
region: validatedData.region,
|
||||
accessKeyId: validatedData.accessKeyId,
|
||||
secretAccessKey: validatedData.secretAccessKey,
|
||||
})
|
||||
|
||||
const startQueryCommand = new StartQueryCommand({
|
||||
logGroupNames: validatedData.logGroupNames,
|
||||
queryString: validatedData.queryString,
|
||||
startTime: validatedData.startTime,
|
||||
endTime: validatedData.endTime,
|
||||
...(validatedData.limit !== undefined && { limit: validatedData.limit }),
|
||||
})
|
||||
|
||||
const startQueryResponse = await client.send(startQueryCommand)
|
||||
const queryId = startQueryResponse.queryId
|
||||
|
||||
if (!queryId) {
|
||||
throw new Error('Failed to start CloudWatch Log Insights query: no queryId returned')
|
||||
}
|
||||
|
||||
const result = await pollQueryResults(client, queryId)
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
output: {
|
||||
results: result.results,
|
||||
statistics: result.statistics,
|
||||
status: result.status,
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : 'CloudWatch Log Insights query failed'
|
||||
logger.error('QueryLogs failed', { error: errorMessage })
|
||||
return NextResponse.json({ error: errorMessage }, { status: 500 })
|
||||
}
|
||||
}
|
||||
161
apps/sim/app/api/tools/cloudwatch/utils.ts
Normal file
161
apps/sim/app/api/tools/cloudwatch/utils.ts
Normal file
@@ -0,0 +1,161 @@
|
||||
import {
|
||||
CloudWatchLogsClient,
|
||||
DescribeLogStreamsCommand,
|
||||
GetLogEventsCommand,
|
||||
GetQueryResultsCommand,
|
||||
type ResultField,
|
||||
} from '@aws-sdk/client-cloudwatch-logs'
|
||||
import { DEFAULT_EXECUTION_TIMEOUT_MS } from '@/lib/core/execution-limits'
|
||||
|
||||
interface AwsCredentials {
|
||||
region: string
|
||||
accessKeyId: string
|
||||
secretAccessKey: string
|
||||
}
|
||||
|
||||
export function createCloudWatchLogsClient(config: AwsCredentials): CloudWatchLogsClient {
|
||||
return new CloudWatchLogsClient({
|
||||
region: config.region,
|
||||
credentials: {
|
||||
accessKeyId: config.accessKeyId,
|
||||
secretAccessKey: config.secretAccessKey,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
interface PollOptions {
|
||||
maxWaitMs?: number
|
||||
pollIntervalMs?: number
|
||||
}
|
||||
|
||||
interface PollResult {
|
||||
results: Record<string, string>[]
|
||||
statistics: {
|
||||
bytesScanned: number
|
||||
recordsMatched: number
|
||||
recordsScanned: number
|
||||
}
|
||||
status: string
|
||||
}
|
||||
|
||||
function parseResultFields(fields: ResultField[] | undefined): Record<string, string> {
|
||||
const record: Record<string, string> = {}
|
||||
if (!fields) return record
|
||||
for (const field of fields) {
|
||||
if (field.field && field.value !== undefined) {
|
||||
record[field.field] = field.value ?? ''
|
||||
}
|
||||
}
|
||||
return record
|
||||
}
|
||||
|
||||
export async function pollQueryResults(
|
||||
client: CloudWatchLogsClient,
|
||||
queryId: string,
|
||||
options: PollOptions = {}
|
||||
): Promise<PollResult> {
|
||||
const { maxWaitMs = DEFAULT_EXECUTION_TIMEOUT_MS, pollIntervalMs = 1_000 } = options
|
||||
const startTime = Date.now()
|
||||
|
||||
while (Date.now() - startTime < maxWaitMs) {
|
||||
const command = new GetQueryResultsCommand({ queryId })
|
||||
const response = await client.send(command)
|
||||
|
||||
const status = response.status ?? 'Unknown'
|
||||
|
||||
if (status === 'Complete') {
|
||||
return {
|
||||
results: (response.results ?? []).map(parseResultFields),
|
||||
statistics: {
|
||||
bytesScanned: response.statistics?.bytesScanned ?? 0,
|
||||
recordsMatched: response.statistics?.recordsMatched ?? 0,
|
||||
recordsScanned: response.statistics?.recordsScanned ?? 0,
|
||||
},
|
||||
status,
|
||||
}
|
||||
}
|
||||
|
||||
if (status === 'Failed' || status === 'Cancelled') {
|
||||
throw new Error(`CloudWatch Log Insights query ${status.toLowerCase()}`)
|
||||
}
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs))
|
||||
}
|
||||
|
||||
// Timeout -- fetch one last time for partial results
|
||||
const finalResponse = await client.send(new GetQueryResultsCommand({ queryId }))
|
||||
return {
|
||||
results: (finalResponse.results ?? []).map(parseResultFields),
|
||||
statistics: {
|
||||
bytesScanned: finalResponse.statistics?.bytesScanned ?? 0,
|
||||
recordsMatched: finalResponse.statistics?.recordsMatched ?? 0,
|
||||
recordsScanned: finalResponse.statistics?.recordsScanned ?? 0,
|
||||
},
|
||||
status: `Timeout (last status: ${finalResponse.status ?? 'Unknown'})`,
|
||||
}
|
||||
}
|
||||
|
||||
export async function describeLogStreams(
|
||||
client: CloudWatchLogsClient,
|
||||
logGroupName: string,
|
||||
options?: { prefix?: string; limit?: number }
|
||||
): Promise<{
|
||||
logStreams: {
|
||||
logStreamName: string
|
||||
lastEventTimestamp: number | undefined
|
||||
firstEventTimestamp: number | undefined
|
||||
creationTime: number | undefined
|
||||
storedBytes: number
|
||||
}[]
|
||||
}> {
|
||||
const hasPrefix = Boolean(options?.prefix)
|
||||
const command = new DescribeLogStreamsCommand({
|
||||
logGroupName,
|
||||
...(hasPrefix
|
||||
? { orderBy: 'LogStreamName', logStreamNamePrefix: options!.prefix }
|
||||
: { orderBy: 'LastEventTime', descending: true }),
|
||||
...(options?.limit !== undefined && { limit: options.limit }),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
return {
|
||||
logStreams: (response.logStreams ?? []).map((ls) => ({
|
||||
logStreamName: ls.logStreamName ?? '',
|
||||
lastEventTimestamp: ls.lastEventTimestamp,
|
||||
firstEventTimestamp: ls.firstEventTimestamp,
|
||||
creationTime: ls.creationTime,
|
||||
storedBytes: ls.storedBytes ?? 0,
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
export async function getLogEvents(
|
||||
client: CloudWatchLogsClient,
|
||||
logGroupName: string,
|
||||
logStreamName: string,
|
||||
options?: { startTime?: number; endTime?: number; limit?: number }
|
||||
): Promise<{
|
||||
events: {
|
||||
timestamp: number | undefined
|
||||
message: string | undefined
|
||||
ingestionTime: number | undefined
|
||||
}[]
|
||||
}> {
|
||||
const command = new GetLogEventsCommand({
|
||||
logGroupIdentifier: logGroupName,
|
||||
logStreamName,
|
||||
...(options?.startTime !== undefined && { startTime: options.startTime * 1000 }),
|
||||
...(options?.endTime !== undefined && { endTime: options.endTime * 1000 }),
|
||||
...(options?.limit !== undefined && { limit: options.limit }),
|
||||
startFromHead: true,
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
return {
|
||||
events: (response.events ?? []).map((e) => ({
|
||||
timestamp: e.timestamp,
|
||||
message: e.message,
|
||||
ingestionTime: e.ingestionTime,
|
||||
})),
|
||||
}
|
||||
}
|
||||
571
apps/sim/blocks/blocks/cloudwatch.ts
Normal file
571
apps/sim/blocks/blocks/cloudwatch.ts
Normal file
@@ -0,0 +1,571 @@
|
||||
import { CloudWatchIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { IntegrationType } from '@/blocks/types'
|
||||
import type {
|
||||
CloudWatchDescribeAlarmsResponse,
|
||||
CloudWatchDescribeLogGroupsResponse,
|
||||
CloudWatchDescribeLogStreamsResponse,
|
||||
CloudWatchGetLogEventsResponse,
|
||||
CloudWatchGetMetricStatisticsResponse,
|
||||
CloudWatchListMetricsResponse,
|
||||
CloudWatchQueryLogsResponse,
|
||||
} from '@/tools/cloudwatch/types'
|
||||
|
||||
export const CloudWatchBlock: BlockConfig<
|
||||
| CloudWatchQueryLogsResponse
|
||||
| CloudWatchDescribeLogGroupsResponse
|
||||
| CloudWatchDescribeLogStreamsResponse
|
||||
| CloudWatchGetLogEventsResponse
|
||||
| CloudWatchDescribeAlarmsResponse
|
||||
| CloudWatchListMetricsResponse
|
||||
| CloudWatchGetMetricStatisticsResponse
|
||||
> = {
|
||||
type: 'cloudwatch',
|
||||
name: 'CloudWatch',
|
||||
description: 'Query and monitor AWS CloudWatch logs, metrics, and alarms',
|
||||
longDescription:
|
||||
'Integrate AWS CloudWatch into workflows. Run Log Insights queries, list log groups, retrieve log events, list and get metrics, and monitor alarms. Requires AWS access key and secret access key.',
|
||||
category: 'tools',
|
||||
integrationType: IntegrationType.Analytics,
|
||||
tags: ['cloud', 'monitoring'],
|
||||
bgColor: 'linear-gradient(45deg, #B0084D 0%, #FF4F8B 100%)',
|
||||
icon: CloudWatchIcon,
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Query Logs (Insights)', id: 'query_logs' },
|
||||
{ label: 'Describe Log Groups', id: 'describe_log_groups' },
|
||||
{ label: 'Get Log Events', id: 'get_log_events' },
|
||||
{ label: 'Describe Log Streams', id: 'describe_log_streams' },
|
||||
{ label: 'List Metrics', id: 'list_metrics' },
|
||||
{ label: 'Get Metric Statistics', id: 'get_metric_statistics' },
|
||||
{ label: 'Describe Alarms', id: 'describe_alarms' },
|
||||
],
|
||||
value: () => 'query_logs',
|
||||
},
|
||||
{
|
||||
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,
|
||||
},
|
||||
// Query Logs fields
|
||||
{
|
||||
id: 'logGroupSelector',
|
||||
title: 'Log Group',
|
||||
type: 'file-selector',
|
||||
canonicalParamId: 'logGroupNames',
|
||||
selectorKey: 'cloudwatch.logGroups',
|
||||
dependsOn: ['awsAccessKeyId', 'awsSecretAccessKey', 'awsRegion'],
|
||||
placeholder: 'Select a log group',
|
||||
condition: { field: 'operation', value: 'query_logs' },
|
||||
required: { field: 'operation', value: 'query_logs' },
|
||||
mode: 'basic',
|
||||
},
|
||||
{
|
||||
id: 'logGroupNamesInput',
|
||||
title: 'Log Group Names',
|
||||
type: 'short-input',
|
||||
canonicalParamId: 'logGroupNames',
|
||||
placeholder: '/aws/lambda/my-func, /aws/ecs/my-service',
|
||||
condition: { field: 'operation', value: 'query_logs' },
|
||||
required: { field: 'operation', value: 'query_logs' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'queryString',
|
||||
title: 'Query',
|
||||
type: 'code',
|
||||
placeholder: 'fields @timestamp, @message\n| sort @timestamp desc\n| limit 20',
|
||||
condition: { field: 'operation', value: 'query_logs' },
|
||||
required: { field: 'operation', value: 'query_logs' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a CloudWatch Log Insights query based on the user's description.
|
||||
The query language supports: fields, filter, stats, sort, limit, parse, display.
|
||||
Common patterns:
|
||||
- fields @timestamp, @message | sort @timestamp desc | limit 20
|
||||
- filter @message like /ERROR/ | stats count(*) by bin(1h)
|
||||
- stats avg(duration) as avgDuration by functionName | sort avgDuration desc
|
||||
- filter @message like /Exception/ | parse @message "* Exception: *" as prefix, errorMsg
|
||||
- stats count(*) as requestCount by status | sort requestCount desc
|
||||
|
||||
Return ONLY the query — no explanations, no markdown code blocks.`,
|
||||
placeholder: 'Describe what you want to find in the logs...',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'startTime',
|
||||
title: 'Start Time (Unix epoch seconds)',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., 1711900800',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['query_logs', 'get_log_events', 'get_metric_statistics'],
|
||||
},
|
||||
required: { field: 'operation', value: ['query_logs', 'get_metric_statistics'] },
|
||||
},
|
||||
{
|
||||
id: 'endTime',
|
||||
title: 'End Time (Unix epoch seconds)',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., 1711987200',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['query_logs', 'get_log_events', 'get_metric_statistics'],
|
||||
},
|
||||
required: { field: 'operation', value: ['query_logs', 'get_metric_statistics'] },
|
||||
},
|
||||
// Describe Log Groups fields
|
||||
{
|
||||
id: 'prefix',
|
||||
title: 'Log Group Name Prefix',
|
||||
type: 'short-input',
|
||||
placeholder: '/aws/lambda/',
|
||||
condition: { field: 'operation', value: 'describe_log_groups' },
|
||||
},
|
||||
// Get Log Events / Describe Log Streams — shared log group selector
|
||||
{
|
||||
id: 'logGroupNameSelector',
|
||||
title: 'Log Group',
|
||||
type: 'file-selector',
|
||||
canonicalParamId: 'logGroupName',
|
||||
selectorKey: 'cloudwatch.logGroups',
|
||||
dependsOn: ['awsAccessKeyId', 'awsSecretAccessKey', 'awsRegion'],
|
||||
placeholder: 'Select a log group',
|
||||
condition: { field: 'operation', value: ['get_log_events', 'describe_log_streams'] },
|
||||
required: { field: 'operation', value: ['get_log_events', 'describe_log_streams'] },
|
||||
mode: 'basic',
|
||||
},
|
||||
{
|
||||
id: 'logGroupNameInput',
|
||||
title: 'Log Group Name',
|
||||
type: 'short-input',
|
||||
canonicalParamId: 'logGroupName',
|
||||
placeholder: '/aws/lambda/my-func',
|
||||
condition: { field: 'operation', value: ['get_log_events', 'describe_log_streams'] },
|
||||
required: { field: 'operation', value: ['get_log_events', 'describe_log_streams'] },
|
||||
mode: 'advanced',
|
||||
},
|
||||
// Describe Log Streams — stream prefix filter
|
||||
{
|
||||
id: 'streamPrefix',
|
||||
title: 'Stream Name Prefix',
|
||||
type: 'short-input',
|
||||
placeholder: '2024/03/31/',
|
||||
condition: { field: 'operation', value: 'describe_log_streams' },
|
||||
},
|
||||
// Get Log Events — log stream selector (cascading: depends on log group)
|
||||
{
|
||||
id: 'logStreamNameSelector',
|
||||
title: 'Log Stream',
|
||||
type: 'file-selector',
|
||||
canonicalParamId: 'logStreamName',
|
||||
selectorKey: 'cloudwatch.logStreams',
|
||||
dependsOn: ['awsAccessKeyId', 'awsSecretAccessKey', 'awsRegion', 'logGroupNameSelector'],
|
||||
placeholder: 'Select a log stream',
|
||||
condition: { field: 'operation', value: 'get_log_events' },
|
||||
required: { field: 'operation', value: 'get_log_events' },
|
||||
mode: 'basic',
|
||||
},
|
||||
{
|
||||
id: 'logStreamNameInput',
|
||||
title: 'Log Stream Name',
|
||||
type: 'short-input',
|
||||
canonicalParamId: 'logStreamName',
|
||||
placeholder: '2024/03/31/[$LATEST]abc123',
|
||||
condition: { field: 'operation', value: 'get_log_events' },
|
||||
required: { field: 'operation', value: 'get_log_events' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
// List Metrics fields
|
||||
{
|
||||
id: 'metricNamespace',
|
||||
title: 'Namespace',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., AWS/EC2, AWS/Lambda, AWS/RDS',
|
||||
condition: { field: 'operation', value: ['list_metrics', 'get_metric_statistics'] },
|
||||
required: { field: 'operation', value: 'get_metric_statistics' },
|
||||
},
|
||||
{
|
||||
id: 'metricName',
|
||||
title: 'Metric Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., CPUUtilization, Invocations',
|
||||
condition: { field: 'operation', value: ['list_metrics', 'get_metric_statistics'] },
|
||||
required: { field: 'operation', value: 'get_metric_statistics' },
|
||||
},
|
||||
{
|
||||
id: 'recentlyActive',
|
||||
title: 'Recently Active Only',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'list_metrics' },
|
||||
},
|
||||
// Get Metric Statistics fields
|
||||
{
|
||||
id: 'metricPeriod',
|
||||
title: 'Period (seconds)',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., 60, 300, 3600',
|
||||
condition: { field: 'operation', value: 'get_metric_statistics' },
|
||||
required: { field: 'operation', value: 'get_metric_statistics' },
|
||||
},
|
||||
{
|
||||
id: 'metricStatistics',
|
||||
title: 'Statistics',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Average', id: 'Average' },
|
||||
{ label: 'Sum', id: 'Sum' },
|
||||
{ label: 'Minimum', id: 'Minimum' },
|
||||
{ label: 'Maximum', id: 'Maximum' },
|
||||
{ label: 'Sample Count', id: 'SampleCount' },
|
||||
],
|
||||
condition: { field: 'operation', value: 'get_metric_statistics' },
|
||||
required: { field: 'operation', value: 'get_metric_statistics' },
|
||||
},
|
||||
{
|
||||
id: 'metricDimensions',
|
||||
title: 'Dimensions',
|
||||
type: 'table',
|
||||
columns: ['name', 'value'],
|
||||
condition: { field: 'operation', value: 'get_metric_statistics' },
|
||||
},
|
||||
// Describe Alarms fields
|
||||
{
|
||||
id: 'alarmNamePrefix',
|
||||
title: 'Alarm Name Prefix',
|
||||
type: 'short-input',
|
||||
placeholder: 'my-service-',
|
||||
condition: { field: 'operation', value: 'describe_alarms' },
|
||||
},
|
||||
{
|
||||
id: 'stateValue',
|
||||
title: 'State',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'All States', id: '' },
|
||||
{ label: 'OK', id: 'OK' },
|
||||
{ label: 'ALARM', id: 'ALARM' },
|
||||
{ label: 'INSUFFICIENT_DATA', id: 'INSUFFICIENT_DATA' },
|
||||
],
|
||||
condition: { field: 'operation', value: 'describe_alarms' },
|
||||
},
|
||||
{
|
||||
id: 'alarmType',
|
||||
title: 'Alarm Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'All Types', id: '' },
|
||||
{ label: 'Metric Alarm', id: 'MetricAlarm' },
|
||||
{ label: 'Composite Alarm', id: 'CompositeAlarm' },
|
||||
],
|
||||
condition: { field: 'operation', value: 'describe_alarms' },
|
||||
},
|
||||
// Shared limit field
|
||||
{
|
||||
id: 'limit',
|
||||
title: 'Limit',
|
||||
type: 'short-input',
|
||||
placeholder: '100',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'query_logs',
|
||||
'describe_log_groups',
|
||||
'get_log_events',
|
||||
'describe_log_streams',
|
||||
'list_metrics',
|
||||
'describe_alarms',
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: [
|
||||
'cloudwatch_query_logs',
|
||||
'cloudwatch_describe_log_groups',
|
||||
'cloudwatch_get_log_events',
|
||||
'cloudwatch_describe_log_streams',
|
||||
'cloudwatch_list_metrics',
|
||||
'cloudwatch_get_metric_statistics',
|
||||
'cloudwatch_describe_alarms',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => {
|
||||
switch (params.operation) {
|
||||
case 'query_logs':
|
||||
return 'cloudwatch_query_logs'
|
||||
case 'describe_log_groups':
|
||||
return 'cloudwatch_describe_log_groups'
|
||||
case 'get_log_events':
|
||||
return 'cloudwatch_get_log_events'
|
||||
case 'describe_log_streams':
|
||||
return 'cloudwatch_describe_log_streams'
|
||||
case 'list_metrics':
|
||||
return 'cloudwatch_list_metrics'
|
||||
case 'get_metric_statistics':
|
||||
return 'cloudwatch_get_metric_statistics'
|
||||
case 'describe_alarms':
|
||||
return 'cloudwatch_describe_alarms'
|
||||
default:
|
||||
throw new Error(`Invalid CloudWatch operation: ${params.operation}`)
|
||||
}
|
||||
},
|
||||
params: (params) => {
|
||||
const { operation, startTime, endTime, limit, ...rest } = params
|
||||
|
||||
const awsRegion = rest.awsRegion
|
||||
const awsAccessKeyId = rest.awsAccessKeyId
|
||||
const awsSecretAccessKey = rest.awsSecretAccessKey
|
||||
const parsedLimit = limit ? Number.parseInt(String(limit), 10) : undefined
|
||||
|
||||
switch (operation) {
|
||||
case 'query_logs': {
|
||||
const logGroupNames = rest.logGroupNames
|
||||
if (!logGroupNames) {
|
||||
throw new Error('Log group names are required')
|
||||
}
|
||||
if (!startTime) {
|
||||
throw new Error('Start time is required')
|
||||
}
|
||||
if (!endTime) {
|
||||
throw new Error('End time is required')
|
||||
}
|
||||
|
||||
const groupNames =
|
||||
typeof logGroupNames === 'string'
|
||||
? logGroupNames
|
||||
.split(',')
|
||||
.map((n: string) => n.trim())
|
||||
.filter(Boolean)
|
||||
: logGroupNames
|
||||
|
||||
return {
|
||||
awsRegion,
|
||||
awsAccessKeyId,
|
||||
awsSecretAccessKey,
|
||||
logGroupNames: groupNames,
|
||||
queryString: rest.queryString,
|
||||
startTime: Number(startTime),
|
||||
endTime: Number(endTime),
|
||||
...(parsedLimit !== undefined && { limit: parsedLimit }),
|
||||
}
|
||||
}
|
||||
|
||||
case 'describe_log_groups':
|
||||
return {
|
||||
awsRegion,
|
||||
awsAccessKeyId,
|
||||
awsSecretAccessKey,
|
||||
...(rest.prefix && { prefix: rest.prefix }),
|
||||
...(parsedLimit !== undefined && { limit: parsedLimit }),
|
||||
}
|
||||
|
||||
case 'get_log_events': {
|
||||
if (!rest.logGroupName) {
|
||||
throw new Error('Log group name is required')
|
||||
}
|
||||
if (!rest.logStreamName) {
|
||||
throw new Error('Log stream name is required')
|
||||
}
|
||||
|
||||
return {
|
||||
awsRegion,
|
||||
awsAccessKeyId,
|
||||
awsSecretAccessKey,
|
||||
logGroupName: rest.logGroupName,
|
||||
logStreamName: rest.logStreamName,
|
||||
...(startTime && { startTime: Number(startTime) }),
|
||||
...(endTime && { endTime: Number(endTime) }),
|
||||
...(parsedLimit !== undefined && { limit: parsedLimit }),
|
||||
}
|
||||
}
|
||||
|
||||
case 'describe_log_streams': {
|
||||
if (!rest.logGroupName) {
|
||||
throw new Error('Log group name is required')
|
||||
}
|
||||
|
||||
return {
|
||||
awsRegion,
|
||||
awsAccessKeyId,
|
||||
awsSecretAccessKey,
|
||||
logGroupName: rest.logGroupName,
|
||||
...(rest.streamPrefix && { prefix: rest.streamPrefix }),
|
||||
...(parsedLimit !== undefined && { limit: parsedLimit }),
|
||||
}
|
||||
}
|
||||
|
||||
case 'list_metrics':
|
||||
return {
|
||||
awsRegion,
|
||||
awsAccessKeyId,
|
||||
awsSecretAccessKey,
|
||||
...(rest.metricNamespace && { namespace: rest.metricNamespace }),
|
||||
...(rest.metricName && { metricName: rest.metricName }),
|
||||
...(rest.recentlyActive && { recentlyActive: true }),
|
||||
...(parsedLimit !== undefined && { limit: parsedLimit }),
|
||||
}
|
||||
|
||||
case 'get_metric_statistics': {
|
||||
if (!rest.metricNamespace) {
|
||||
throw new Error('Namespace is required')
|
||||
}
|
||||
if (!rest.metricName) {
|
||||
throw new Error('Metric name is required')
|
||||
}
|
||||
if (!startTime) {
|
||||
throw new Error('Start time is required')
|
||||
}
|
||||
if (!endTime) {
|
||||
throw new Error('End time is required')
|
||||
}
|
||||
if (!rest.metricPeriod) {
|
||||
throw new Error('Period is required')
|
||||
}
|
||||
|
||||
const stat = rest.metricStatistics
|
||||
if (!stat) {
|
||||
throw new Error('Statistics is required')
|
||||
}
|
||||
|
||||
return {
|
||||
awsRegion,
|
||||
awsAccessKeyId,
|
||||
awsSecretAccessKey,
|
||||
namespace: rest.metricNamespace,
|
||||
metricName: rest.metricName,
|
||||
startTime: Number(startTime),
|
||||
endTime: Number(endTime),
|
||||
period: Number(rest.metricPeriod),
|
||||
statistics: Array.isArray(stat) ? stat : [stat],
|
||||
...(rest.metricDimensions && {
|
||||
dimensions: (() => {
|
||||
const dims = rest.metricDimensions
|
||||
if (typeof dims === 'string') return dims
|
||||
if (Array.isArray(dims)) {
|
||||
const obj: Record<string, string> = {}
|
||||
for (const row of dims) {
|
||||
const name = row.cells?.name
|
||||
const value = row.cells?.value
|
||||
if (name && value !== undefined) obj[name] = String(value)
|
||||
}
|
||||
return JSON.stringify(obj)
|
||||
}
|
||||
return JSON.stringify(dims)
|
||||
})(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
case 'describe_alarms':
|
||||
return {
|
||||
awsRegion,
|
||||
awsAccessKeyId,
|
||||
awsSecretAccessKey,
|
||||
...(rest.alarmNamePrefix && { alarmNamePrefix: rest.alarmNamePrefix }),
|
||||
...(rest.stateValue && { stateValue: rest.stateValue }),
|
||||
...(rest.alarmType && { alarmType: rest.alarmType }),
|
||||
...(parsedLimit !== undefined && { limit: parsedLimit }),
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`Invalid CloudWatch operation: ${operation}`)
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'CloudWatch 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' },
|
||||
logGroupNames: { type: 'string', description: 'Log group name(s) for query' },
|
||||
queryString: { type: 'string', description: 'CloudWatch Log Insights query string' },
|
||||
startTime: { type: 'string', description: 'Start time as Unix epoch seconds' },
|
||||
endTime: { type: 'string', description: 'End time as Unix epoch seconds' },
|
||||
prefix: { type: 'string', description: 'Log group name prefix filter' },
|
||||
logGroupName: {
|
||||
type: 'string',
|
||||
description: 'Log group name for get events / describe streams',
|
||||
},
|
||||
logStreamName: { type: 'string', description: 'Log stream name for get events' },
|
||||
streamPrefix: { type: 'string', description: 'Log stream name prefix filter' },
|
||||
metricNamespace: { type: 'string', description: 'Metric namespace (e.g., AWS/EC2)' },
|
||||
metricName: { type: 'string', description: 'Metric name (e.g., CPUUtilization)' },
|
||||
recentlyActive: { type: 'boolean', description: 'Only show recently active metrics' },
|
||||
metricPeriod: { type: 'number', description: 'Granularity in seconds' },
|
||||
metricStatistics: { type: 'string', description: 'Statistic type (Average, Sum, etc.)' },
|
||||
metricDimensions: { type: 'json', description: 'Metric dimensions (Name/Value pairs)' },
|
||||
alarmNamePrefix: { type: 'string', description: 'Alarm name prefix filter' },
|
||||
stateValue: {
|
||||
type: 'string',
|
||||
description: 'Alarm state filter (OK, ALARM, INSUFFICIENT_DATA)',
|
||||
},
|
||||
alarmType: { type: 'string', description: 'Alarm type filter (MetricAlarm, CompositeAlarm)' },
|
||||
limit: { type: 'number', description: 'Maximum number of results' },
|
||||
},
|
||||
outputs: {
|
||||
results: {
|
||||
type: 'array',
|
||||
description: 'Log Insights query result rows',
|
||||
},
|
||||
statistics: {
|
||||
type: 'json',
|
||||
description: 'Query statistics (bytesScanned, recordsMatched, recordsScanned)',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Query completion status',
|
||||
},
|
||||
logGroups: {
|
||||
type: 'array',
|
||||
description: 'List of CloudWatch log groups',
|
||||
},
|
||||
events: {
|
||||
type: 'array',
|
||||
description: 'Log events with timestamp and message',
|
||||
},
|
||||
logStreams: {
|
||||
type: 'array',
|
||||
description: 'Log streams with metadata',
|
||||
},
|
||||
metrics: {
|
||||
type: 'array',
|
||||
description: 'List of available metrics',
|
||||
},
|
||||
label: {
|
||||
type: 'string',
|
||||
description: 'Metric label',
|
||||
},
|
||||
datapoints: {
|
||||
type: 'array',
|
||||
description: 'Metric datapoints with timestamps and values',
|
||||
},
|
||||
alarms: {
|
||||
type: 'array',
|
||||
description: 'CloudWatch alarms with state and configuration',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import { CirclebackBlock } from '@/blocks/blocks/circleback'
|
||||
import { ClayBlock } from '@/blocks/blocks/clay'
|
||||
import { ClerkBlock } from '@/blocks/blocks/clerk'
|
||||
import { CloudflareBlock } from '@/blocks/blocks/cloudflare'
|
||||
import { CloudWatchBlock } from '@/blocks/blocks/cloudwatch'
|
||||
import { ConditionBlock } from '@/blocks/blocks/condition'
|
||||
import { ConfluenceBlock, ConfluenceV2Block } from '@/blocks/blocks/confluence'
|
||||
import { CredentialBlock } from '@/blocks/blocks/credential'
|
||||
@@ -241,6 +242,7 @@ export const registry: Record<string, BlockConfig> = {
|
||||
chat_trigger: ChatTriggerBlock,
|
||||
circleback: CirclebackBlock,
|
||||
cloudflare: CloudflareBlock,
|
||||
cloudwatch: CloudWatchBlock,
|
||||
clay: ClayBlock,
|
||||
clerk: ClerkBlock,
|
||||
condition: ConditionBlock,
|
||||
|
||||
@@ -4653,6 +4653,33 @@ export function SQSIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function CloudWatchIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
viewBox='0 0 80 80'
|
||||
version='1.1'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
xmlnsXlink='http://www.w3.org/1999/xlink'
|
||||
>
|
||||
<g
|
||||
id='Icon-Architecture/64/Arch_Amazon-CloudWatch_64'
|
||||
stroke='none'
|
||||
strokeWidth='1'
|
||||
fill='none'
|
||||
fillRule='evenodd'
|
||||
transform='translate(40, 40) scale(1.25) translate(-40, -40)'
|
||||
>
|
||||
<path
|
||||
d='M53,42 L41,42 L41,24 L43,24 L43,40 L53,40 L53,42 Z M40,66 C24.561,66 12,53.439 12,38 C12,22.561 24.561,10 40,10 C55.439,10 68,22.561 68,38 C68,53.439 55.439,66 40,66 M40,8 C23.458,8 10,21.458 10,38 C10,54.542 23.458,68 40,68 C56.542,68 70,54.542 70,38 C70,21.458 56.542,8 40,8'
|
||||
id='Amazon-CloudWatch_Icon_64_Squid'
|
||||
fill='currentColor'
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function TextractIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
|
||||
@@ -1716,6 +1716,81 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
}))
|
||||
},
|
||||
},
|
||||
'cloudwatch.logGroups': {
|
||||
key: 'cloudwatch.logGroups',
|
||||
staleTime: SELECTOR_STALE,
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'cloudwatch.logGroups',
|
||||
context.awsAccessKeyId ?? 'none',
|
||||
context.awsRegion ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) =>
|
||||
Boolean(context.awsAccessKeyId && context.awsSecretAccessKey && context.awsRegion),
|
||||
fetchList: async ({ context, search }: SelectorQueryArgs) => {
|
||||
const body = JSON.stringify({
|
||||
accessKeyId: context.awsAccessKeyId,
|
||||
secretAccessKey: context.awsSecretAccessKey,
|
||||
region: context.awsRegion,
|
||||
...(search && { prefix: search }),
|
||||
})
|
||||
const data = await fetchJson<{
|
||||
output: { logGroups: { logGroupName: string }[] }
|
||||
}>('/api/tools/cloudwatch/describe-log-groups', {
|
||||
method: 'POST',
|
||||
body,
|
||||
})
|
||||
return (data.output?.logGroups || []).map((lg) => ({
|
||||
id: lg.logGroupName,
|
||||
label: lg.logGroupName,
|
||||
}))
|
||||
},
|
||||
fetchById: async ({ detailId }: SelectorQueryArgs) => {
|
||||
if (!detailId) return null
|
||||
return { id: detailId, label: detailId }
|
||||
},
|
||||
},
|
||||
'cloudwatch.logStreams': {
|
||||
key: 'cloudwatch.logStreams',
|
||||
staleTime: SELECTOR_STALE,
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'cloudwatch.logStreams',
|
||||
context.awsAccessKeyId ?? 'none',
|
||||
context.awsRegion ?? 'none',
|
||||
context.logGroupName ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) =>
|
||||
Boolean(
|
||||
context.awsAccessKeyId &&
|
||||
context.awsSecretAccessKey &&
|
||||
context.awsRegion &&
|
||||
context.logGroupName
|
||||
),
|
||||
fetchList: async ({ context, search }: SelectorQueryArgs) => {
|
||||
const body = JSON.stringify({
|
||||
accessKeyId: context.awsAccessKeyId,
|
||||
secretAccessKey: context.awsSecretAccessKey,
|
||||
region: context.awsRegion,
|
||||
logGroupName: context.logGroupName,
|
||||
...(search && { prefix: search }),
|
||||
})
|
||||
const data = await fetchJson<{
|
||||
output: { logStreams: { logStreamName: string; lastEventTimestamp?: number }[] }
|
||||
}>('/api/tools/cloudwatch/describe-log-streams', {
|
||||
method: 'POST',
|
||||
body,
|
||||
})
|
||||
return (data.output?.logStreams || []).map((ls) => ({
|
||||
id: ls.logStreamName,
|
||||
label: ls.logStreamName,
|
||||
}))
|
||||
},
|
||||
fetchById: async ({ detailId }: SelectorQueryArgs) => {
|
||||
if (!detailId) return null
|
||||
return { id: detailId, label: detailId }
|
||||
},
|
||||
},
|
||||
'sim.workflows': {
|
||||
key: 'sim.workflows',
|
||||
staleTime: SELECTOR_STALE,
|
||||
|
||||
@@ -49,6 +49,8 @@ export type SelectorKey =
|
||||
| 'webflow.sites'
|
||||
| 'webflow.collections'
|
||||
| 'webflow.items'
|
||||
| 'cloudwatch.logGroups'
|
||||
| 'cloudwatch.logStreams'
|
||||
| 'sim.workflows'
|
||||
|
||||
export interface SelectorOption {
|
||||
@@ -78,6 +80,10 @@ export interface SelectorContext {
|
||||
datasetId?: string
|
||||
serviceDeskId?: string
|
||||
impersonateUserEmail?: string
|
||||
awsAccessKeyId?: string
|
||||
awsSecretAccessKey?: string
|
||||
awsRegion?: string
|
||||
logGroupName?: string
|
||||
}
|
||||
|
||||
export interface SelectorQueryArgs {
|
||||
|
||||
@@ -22,6 +22,10 @@ export const SELECTOR_CONTEXT_FIELDS = new Set<keyof SelectorContext>([
|
||||
'datasetId',
|
||||
'serviceDeskId',
|
||||
'impersonateUserEmail',
|
||||
'awsAccessKeyId',
|
||||
'awsSecretAccessKey',
|
||||
'awsRegion',
|
||||
'logGroupName',
|
||||
])
|
||||
|
||||
/**
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
"@a2a-js/sdk": "0.3.7",
|
||||
"@anthropic-ai/sdk": "0.71.2",
|
||||
"@aws-sdk/client-bedrock-runtime": "3.940.0",
|
||||
"@aws-sdk/client-cloudwatch": "3.940.0",
|
||||
"@aws-sdk/client-cloudwatch-logs": "3.940.0",
|
||||
"@aws-sdk/client-dynamodb": "3.940.0",
|
||||
"@aws-sdk/client-rds-data": "3.940.0",
|
||||
"@aws-sdk/client-s3": "^3.779.0",
|
||||
|
||||
99
apps/sim/tools/cloudwatch/describe_alarms.ts
Normal file
99
apps/sim/tools/cloudwatch/describe_alarms.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import type {
|
||||
CloudWatchDescribeAlarmsParams,
|
||||
CloudWatchDescribeAlarmsResponse,
|
||||
} from '@/tools/cloudwatch/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const describeAlarmsTool: ToolConfig<
|
||||
CloudWatchDescribeAlarmsParams,
|
||||
CloudWatchDescribeAlarmsResponse
|
||||
> = {
|
||||
id: 'cloudwatch_describe_alarms',
|
||||
name: 'CloudWatch Describe Alarms',
|
||||
description: 'List and filter CloudWatch alarms',
|
||||
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',
|
||||
},
|
||||
alarmNamePrefix: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter alarms by name prefix',
|
||||
},
|
||||
stateValue: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by alarm state (OK, ALARM, INSUFFICIENT_DATA)',
|
||||
},
|
||||
alarmType: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by alarm type (MetricAlarm, CompositeAlarm)',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of alarms to return',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: '/api/tools/cloudwatch/describe-alarms',
|
||||
method: 'POST',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => ({
|
||||
region: params.awsRegion,
|
||||
accessKeyId: params.awsAccessKeyId,
|
||||
secretAccessKey: params.awsSecretAccessKey,
|
||||
...(params.alarmNamePrefix && { alarmNamePrefix: params.alarmNamePrefix }),
|
||||
...(params.stateValue && { stateValue: params.stateValue }),
|
||||
...(params.alarmType && { alarmType: params.alarmType }),
|
||||
...(params.limit !== undefined && { limit: params.limit }),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || 'Failed to describe CloudWatch alarms')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
alarms: data.output.alarms,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
alarms: {
|
||||
type: 'array',
|
||||
description: 'List of CloudWatch alarms with state and configuration',
|
||||
},
|
||||
},
|
||||
}
|
||||
82
apps/sim/tools/cloudwatch/describe_log_groups.ts
Normal file
82
apps/sim/tools/cloudwatch/describe_log_groups.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import type {
|
||||
CloudWatchDescribeLogGroupsParams,
|
||||
CloudWatchDescribeLogGroupsResponse,
|
||||
} from '@/tools/cloudwatch/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const describeLogGroupsTool: ToolConfig<
|
||||
CloudWatchDescribeLogGroupsParams,
|
||||
CloudWatchDescribeLogGroupsResponse
|
||||
> = {
|
||||
id: 'cloudwatch_describe_log_groups',
|
||||
name: 'CloudWatch Describe Log Groups',
|
||||
description: 'List available CloudWatch log groups',
|
||||
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',
|
||||
},
|
||||
prefix: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter log groups by name prefix',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of log groups to return',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: '/api/tools/cloudwatch/describe-log-groups',
|
||||
method: 'POST',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => ({
|
||||
region: params.awsRegion,
|
||||
accessKeyId: params.awsAccessKeyId,
|
||||
secretAccessKey: params.awsSecretAccessKey,
|
||||
...(params.prefix && { prefix: params.prefix }),
|
||||
...(params.limit !== undefined && { limit: params.limit }),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || 'Failed to describe CloudWatch log groups')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
logGroups: data.output.logGroups,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
logGroups: { type: 'array', description: 'List of CloudWatch log groups with metadata' },
|
||||
},
|
||||
}
|
||||
92
apps/sim/tools/cloudwatch/describe_log_streams.ts
Normal file
92
apps/sim/tools/cloudwatch/describe_log_streams.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import type {
|
||||
CloudWatchDescribeLogStreamsParams,
|
||||
CloudWatchDescribeLogStreamsResponse,
|
||||
} from '@/tools/cloudwatch/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const describeLogStreamsTool: ToolConfig<
|
||||
CloudWatchDescribeLogStreamsParams,
|
||||
CloudWatchDescribeLogStreamsResponse
|
||||
> = {
|
||||
id: 'cloudwatch_describe_log_streams',
|
||||
name: 'CloudWatch Describe Log Streams',
|
||||
description: 'List log streams within a CloudWatch log group',
|
||||
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',
|
||||
},
|
||||
logGroupName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'CloudWatch log group name',
|
||||
},
|
||||
prefix: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter log streams by name prefix',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of log streams to return',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: '/api/tools/cloudwatch/describe-log-streams',
|
||||
method: 'POST',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => ({
|
||||
region: params.awsRegion,
|
||||
accessKeyId: params.awsAccessKeyId,
|
||||
secretAccessKey: params.awsSecretAccessKey,
|
||||
logGroupName: params.logGroupName,
|
||||
...(params.prefix && { prefix: params.prefix }),
|
||||
...(params.limit !== undefined && { limit: params.limit }),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || 'Failed to describe CloudWatch log streams')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
logStreams: data.output.logStreams,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
logStreams: {
|
||||
type: 'array',
|
||||
description: 'List of log streams with metadata',
|
||||
},
|
||||
},
|
||||
}
|
||||
106
apps/sim/tools/cloudwatch/get_log_events.ts
Normal file
106
apps/sim/tools/cloudwatch/get_log_events.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import type {
|
||||
CloudWatchGetLogEventsParams,
|
||||
CloudWatchGetLogEventsResponse,
|
||||
} from '@/tools/cloudwatch/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const getLogEventsTool: ToolConfig<
|
||||
CloudWatchGetLogEventsParams,
|
||||
CloudWatchGetLogEventsResponse
|
||||
> = {
|
||||
id: 'cloudwatch_get_log_events',
|
||||
name: 'CloudWatch Get Log Events',
|
||||
description: 'Retrieve log events from a specific CloudWatch log stream',
|
||||
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',
|
||||
},
|
||||
logGroupName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'CloudWatch log group name',
|
||||
},
|
||||
logStreamName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'CloudWatch log stream name',
|
||||
},
|
||||
startTime: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Start time as Unix epoch seconds',
|
||||
},
|
||||
endTime: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'End time as Unix epoch seconds',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of events to return',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: '/api/tools/cloudwatch/get-log-events',
|
||||
method: 'POST',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => ({
|
||||
region: params.awsRegion,
|
||||
accessKeyId: params.awsAccessKeyId,
|
||||
secretAccessKey: params.awsSecretAccessKey,
|
||||
logGroupName: params.logGroupName,
|
||||
logStreamName: params.logStreamName,
|
||||
...(params.startTime !== undefined && { startTime: params.startTime }),
|
||||
...(params.endTime !== undefined && { endTime: params.endTime }),
|
||||
...(params.limit !== undefined && { limit: params.limit }),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || 'Failed to get CloudWatch log events')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
events: data.output.events,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
events: {
|
||||
type: 'array',
|
||||
description: 'Log events with timestamp, message, and ingestion time',
|
||||
},
|
||||
},
|
||||
}
|
||||
119
apps/sim/tools/cloudwatch/get_metric_statistics.ts
Normal file
119
apps/sim/tools/cloudwatch/get_metric_statistics.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import type {
|
||||
CloudWatchGetMetricStatisticsParams,
|
||||
CloudWatchGetMetricStatisticsResponse,
|
||||
} from '@/tools/cloudwatch/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const getMetricStatisticsTool: ToolConfig<
|
||||
CloudWatchGetMetricStatisticsParams,
|
||||
CloudWatchGetMetricStatisticsResponse
|
||||
> = {
|
||||
id: 'cloudwatch_get_metric_statistics',
|
||||
name: 'CloudWatch Get Metric Statistics',
|
||||
description: 'Get statistics for a CloudWatch metric over a time range',
|
||||
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',
|
||||
},
|
||||
namespace: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Metric namespace (e.g., AWS/EC2, AWS/Lambda)',
|
||||
},
|
||||
metricName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Metric name (e.g., CPUUtilization, Invocations)',
|
||||
},
|
||||
startTime: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Start time as Unix epoch seconds',
|
||||
},
|
||||
endTime: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'End time as Unix epoch seconds',
|
||||
},
|
||||
period: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Granularity in seconds (e.g., 60, 300, 3600)',
|
||||
},
|
||||
statistics: {
|
||||
type: 'array',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Statistics to retrieve (Average, Sum, Minimum, Maximum, SampleCount)',
|
||||
},
|
||||
dimensions: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Dimensions as JSON (e.g., {"InstanceId": "i-1234"})',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: '/api/tools/cloudwatch/get-metric-statistics',
|
||||
method: 'POST',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => ({
|
||||
region: params.awsRegion,
|
||||
accessKeyId: params.awsAccessKeyId,
|
||||
secretAccessKey: params.awsSecretAccessKey,
|
||||
namespace: params.namespace,
|
||||
metricName: params.metricName,
|
||||
startTime: params.startTime,
|
||||
endTime: params.endTime,
|
||||
period: params.period,
|
||||
statistics: params.statistics,
|
||||
...(params.dimensions && { dimensions: params.dimensions }),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || 'Failed to get CloudWatch metric statistics')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
label: data.output.label,
|
||||
datapoints: data.output.datapoints,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
label: { type: 'string', description: 'Metric label' },
|
||||
datapoints: { type: 'array', description: 'Datapoints with timestamp and statistics values' },
|
||||
},
|
||||
}
|
||||
15
apps/sim/tools/cloudwatch/index.ts
Normal file
15
apps/sim/tools/cloudwatch/index.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { describeAlarmsTool } from '@/tools/cloudwatch/describe_alarms'
|
||||
import { describeLogGroupsTool } from '@/tools/cloudwatch/describe_log_groups'
|
||||
import { describeLogStreamsTool } from '@/tools/cloudwatch/describe_log_streams'
|
||||
import { getLogEventsTool } from '@/tools/cloudwatch/get_log_events'
|
||||
import { getMetricStatisticsTool } from '@/tools/cloudwatch/get_metric_statistics'
|
||||
import { listMetricsTool } from '@/tools/cloudwatch/list_metrics'
|
||||
import { queryLogsTool } from '@/tools/cloudwatch/query_logs'
|
||||
|
||||
export const cloudwatchDescribeAlarmsTool = describeAlarmsTool
|
||||
export const cloudwatchDescribeLogGroupsTool = describeLogGroupsTool
|
||||
export const cloudwatchDescribeLogStreamsTool = describeLogStreamsTool
|
||||
export const cloudwatchGetLogEventsTool = getLogEventsTool
|
||||
export const cloudwatchGetMetricStatisticsTool = getMetricStatisticsTool
|
||||
export const cloudwatchListMetricsTool = listMetricsTool
|
||||
export const cloudwatchQueryLogsTool = queryLogsTool
|
||||
96
apps/sim/tools/cloudwatch/list_metrics.ts
Normal file
96
apps/sim/tools/cloudwatch/list_metrics.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import type {
|
||||
CloudWatchListMetricsParams,
|
||||
CloudWatchListMetricsResponse,
|
||||
} from '@/tools/cloudwatch/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listMetricsTool: ToolConfig<
|
||||
CloudWatchListMetricsParams,
|
||||
CloudWatchListMetricsResponse
|
||||
> = {
|
||||
id: 'cloudwatch_list_metrics',
|
||||
name: 'CloudWatch List Metrics',
|
||||
description: 'List available CloudWatch metrics',
|
||||
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',
|
||||
},
|
||||
namespace: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by namespace (e.g., AWS/EC2, AWS/Lambda)',
|
||||
},
|
||||
metricName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by metric name',
|
||||
},
|
||||
recentlyActive: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only show metrics active in the last 3 hours',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of metrics to return',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: '/api/tools/cloudwatch/list-metrics',
|
||||
method: 'POST',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => ({
|
||||
region: params.awsRegion,
|
||||
accessKeyId: params.awsAccessKeyId,
|
||||
secretAccessKey: params.awsSecretAccessKey,
|
||||
...(params.namespace && { namespace: params.namespace }),
|
||||
...(params.metricName && { metricName: params.metricName }),
|
||||
...(params.recentlyActive && { recentlyActive: params.recentlyActive }),
|
||||
...(params.limit !== undefined && { limit: params.limit }),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || 'Failed to list CloudWatch metrics')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
metrics: data.output.metrics,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
metrics: { type: 'array', description: 'List of metrics with namespace, name, and dimensions' },
|
||||
},
|
||||
}
|
||||
107
apps/sim/tools/cloudwatch/query_logs.ts
Normal file
107
apps/sim/tools/cloudwatch/query_logs.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import type {
|
||||
CloudWatchQueryLogsParams,
|
||||
CloudWatchQueryLogsResponse,
|
||||
} from '@/tools/cloudwatch/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const queryLogsTool: ToolConfig<CloudWatchQueryLogsParams, CloudWatchQueryLogsResponse> = {
|
||||
id: 'cloudwatch_query_logs',
|
||||
name: 'CloudWatch Query Logs',
|
||||
description: 'Run a CloudWatch Log Insights query against one or more log groups',
|
||||
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',
|
||||
},
|
||||
logGroupNames: {
|
||||
type: 'array',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Log group names to query',
|
||||
},
|
||||
queryString: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'CloudWatch Log Insights query string',
|
||||
},
|
||||
startTime: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Start time as Unix epoch seconds',
|
||||
},
|
||||
endTime: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'End time as Unix epoch seconds',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of results to return',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: '/api/tools/cloudwatch/query-logs',
|
||||
method: 'POST',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => ({
|
||||
region: params.awsRegion,
|
||||
accessKeyId: params.awsAccessKeyId,
|
||||
secretAccessKey: params.awsSecretAccessKey,
|
||||
logGroupNames: params.logGroupNames,
|
||||
queryString: params.queryString,
|
||||
startTime: params.startTime,
|
||||
endTime: params.endTime,
|
||||
...(params.limit !== undefined && { limit: params.limit }),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || 'CloudWatch Log Insights query failed')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
results: data.output.results,
|
||||
statistics: data.output.statistics,
|
||||
status: data.output.status,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
results: { type: 'array', description: 'Query result rows' },
|
||||
statistics: {
|
||||
type: 'object',
|
||||
description: 'Query statistics (bytesScanned, recordsMatched, recordsScanned)',
|
||||
},
|
||||
status: { type: 'string', description: 'Query completion status' },
|
||||
},
|
||||
}
|
||||
146
apps/sim/tools/cloudwatch/types.ts
Normal file
146
apps/sim/tools/cloudwatch/types.ts
Normal file
@@ -0,0 +1,146 @@
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface CloudWatchConnectionConfig {
|
||||
awsRegion: string
|
||||
awsAccessKeyId: string
|
||||
awsSecretAccessKey: string
|
||||
}
|
||||
|
||||
export interface CloudWatchQueryLogsParams extends CloudWatchConnectionConfig {
|
||||
logGroupNames: string[]
|
||||
queryString: string
|
||||
startTime: number
|
||||
endTime: number
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface CloudWatchDescribeLogGroupsParams extends CloudWatchConnectionConfig {
|
||||
prefix?: string
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface CloudWatchGetLogEventsParams extends CloudWatchConnectionConfig {
|
||||
logGroupName: string
|
||||
logStreamName: string
|
||||
startTime?: number
|
||||
endTime?: number
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface CloudWatchQueryLogsResponse extends ToolResponse {
|
||||
output: {
|
||||
results: Record<string, string>[]
|
||||
statistics: {
|
||||
bytesScanned: number
|
||||
recordsMatched: number
|
||||
recordsScanned: number
|
||||
}
|
||||
status: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface CloudWatchDescribeLogGroupsResponse extends ToolResponse {
|
||||
output: {
|
||||
logGroups: {
|
||||
logGroupName: string
|
||||
arn: string
|
||||
storedBytes: number
|
||||
retentionInDays: number | undefined
|
||||
creationTime: number | undefined
|
||||
}[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface CloudWatchGetLogEventsResponse extends ToolResponse {
|
||||
output: {
|
||||
events: {
|
||||
timestamp: number | undefined
|
||||
message: string | undefined
|
||||
ingestionTime: number | undefined
|
||||
}[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface CloudWatchDescribeLogStreamsParams extends CloudWatchConnectionConfig {
|
||||
logGroupName: string
|
||||
prefix?: string
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface CloudWatchDescribeLogStreamsResponse extends ToolResponse {
|
||||
output: {
|
||||
logStreams: {
|
||||
logStreamName: string
|
||||
lastEventTimestamp: number | undefined
|
||||
firstEventTimestamp: number | undefined
|
||||
creationTime: number | undefined
|
||||
storedBytes: number
|
||||
}[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface CloudWatchListMetricsParams extends CloudWatchConnectionConfig {
|
||||
namespace?: string
|
||||
metricName?: string
|
||||
recentlyActive?: boolean
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface CloudWatchListMetricsResponse extends ToolResponse {
|
||||
output: {
|
||||
metrics: {
|
||||
namespace: string
|
||||
metricName: string
|
||||
dimensions: { name: string; value: string }[]
|
||||
}[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface CloudWatchGetMetricStatisticsParams extends CloudWatchConnectionConfig {
|
||||
namespace: string
|
||||
metricName: string
|
||||
startTime: number
|
||||
endTime: number
|
||||
period: number
|
||||
statistics: string[]
|
||||
dimensions?: string
|
||||
}
|
||||
|
||||
export interface CloudWatchGetMetricStatisticsResponse extends ToolResponse {
|
||||
output: {
|
||||
label: string
|
||||
datapoints: {
|
||||
timestamp: number
|
||||
average?: number
|
||||
sum?: number
|
||||
minimum?: number
|
||||
maximum?: number
|
||||
sampleCount?: number
|
||||
unit?: string
|
||||
}[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface CloudWatchDescribeAlarmsParams extends CloudWatchConnectionConfig {
|
||||
alarmNamePrefix?: string
|
||||
stateValue?: string
|
||||
alarmType?: string
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface CloudWatchDescribeAlarmsResponse extends ToolResponse {
|
||||
output: {
|
||||
alarms: {
|
||||
alarmName: string
|
||||
alarmArn: string
|
||||
stateValue: string
|
||||
stateReason: string
|
||||
metricName: string | undefined
|
||||
namespace: string | undefined
|
||||
comparisonOperator: string | undefined
|
||||
threshold: number | undefined
|
||||
evaluationPeriods: number | undefined
|
||||
stateUpdatedTimestamp: number | undefined
|
||||
}[]
|
||||
}
|
||||
}
|
||||
@@ -275,6 +275,15 @@ import {
|
||||
cloudflareUpdateDnsRecordTool,
|
||||
cloudflareUpdateZoneSettingTool,
|
||||
} from '@/tools/cloudflare'
|
||||
import {
|
||||
cloudwatchDescribeAlarmsTool,
|
||||
cloudwatchDescribeLogGroupsTool,
|
||||
cloudwatchDescribeLogStreamsTool,
|
||||
cloudwatchGetLogEventsTool,
|
||||
cloudwatchGetMetricStatisticsTool,
|
||||
cloudwatchListMetricsTool,
|
||||
cloudwatchQueryLogsTool,
|
||||
} from '@/tools/cloudwatch'
|
||||
import {
|
||||
confluenceAddLabelTool,
|
||||
confluenceCreateBlogPostTool,
|
||||
@@ -3376,6 +3385,13 @@ export const tools: Record<string, ToolConfig> = {
|
||||
rds_delete: rdsDeleteTool,
|
||||
rds_execute: rdsExecuteTool,
|
||||
rds_introspect: rdsIntrospectTool,
|
||||
cloudwatch_query_logs: cloudwatchQueryLogsTool,
|
||||
cloudwatch_describe_log_groups: cloudwatchDescribeLogGroupsTool,
|
||||
cloudwatch_describe_alarms: cloudwatchDescribeAlarmsTool,
|
||||
cloudwatch_describe_log_streams: cloudwatchDescribeLogStreamsTool,
|
||||
cloudwatch_get_log_events: cloudwatchGetLogEventsTool,
|
||||
cloudwatch_list_metrics: cloudwatchListMetricsTool,
|
||||
cloudwatch_get_metric_statistics: cloudwatchGetMetricStatisticsTool,
|
||||
dynamodb_get: dynamodbGetTool,
|
||||
dynamodb_put: dynamodbPutTool,
|
||||
dynamodb_query: dynamodbQueryTool,
|
||||
|
||||
16
bun.lock
16
bun.lock
@@ -57,6 +57,8 @@
|
||||
"@a2a-js/sdk": "0.3.7",
|
||||
"@anthropic-ai/sdk": "0.71.2",
|
||||
"@aws-sdk/client-bedrock-runtime": "3.940.0",
|
||||
"@aws-sdk/client-cloudwatch": "3.940.0",
|
||||
"@aws-sdk/client-cloudwatch-logs": "3.940.0",
|
||||
"@aws-sdk/client-dynamodb": "3.940.0",
|
||||
"@aws-sdk/client-rds-data": "3.940.0",
|
||||
"@aws-sdk/client-s3": "^3.779.0",
|
||||
@@ -414,6 +416,10 @@
|
||||
|
||||
"@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-cloudwatch": ["@aws-sdk/client-cloudwatch@3.940.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.940.0", "@aws-sdk/credential-provider-node": "3.940.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.936.0", "@aws-sdk/middleware-user-agent": "3.940.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.940.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.5", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-compression": "^4.3.12", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.12", "@smithy/middleware-retry": "^4.4.12", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.11", "@smithy/util-defaults-mode-node": "^4.2.14", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "@smithy/util-waiter": "^4.2.5", "tslib": "^2.6.2" } }, "sha512-C35xpPntRAGdEg3X5iKpSUCBaP3yxYNo1U95qipN/X1e0/TYIDWHwGt8Z1ntRafK19jp5oVzhRQ+PD1JAPSEzA=="],
|
||||
|
||||
"@aws-sdk/client-cloudwatch-logs": ["@aws-sdk/client-cloudwatch-logs@3.940.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.940.0", "@aws-sdk/credential-provider-node": "3.940.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.936.0", "@aws-sdk/middleware-user-agent": "3.940.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.940.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.5", "@smithy/eventstream-serde-browser": "^4.2.5", "@smithy/eventstream-serde-config-resolver": "^4.3.5", "@smithy/eventstream-serde-node": "^4.2.5", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.12", "@smithy/middleware-retry": "^4.4.12", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.11", "@smithy/util-defaults-mode-node": "^4.2.14", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7dEIO3D98IxA9IhqixPJbzQsBkk4TchHHpFdd0JOhlSlihWhiwbf3ijUePJVXYJxcpRRtMmAMtDRLDzCSO+ZHg=="],
|
||||
|
||||
"@aws-sdk/client-dynamodb": ["@aws-sdk/client-dynamodb@3.940.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.940.0", "@aws-sdk/credential-provider-node": "3.940.0", "@aws-sdk/middleware-endpoint-discovery": "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/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", "@smithy/util-waiter": "^4.2.5", "tslib": "^2.6.2" } }, "sha512-u2sXsNJazJbuHeWICvsj6RvNyJh3isedEfPvB21jK/kxcriK+dE/izlKC2cyxUjERCmku0zTFNzY9FhrLbYHjQ=="],
|
||||
|
||||
"@aws-sdk/client-rds-data": ["@aws-sdk/client-rds-data@3.940.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.940.0", "@aws-sdk/credential-provider-node": "3.940.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.936.0", "@aws-sdk/middleware-user-agent": "3.940.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.940.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.5", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-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-68NH61MvS48CVPfzBNCPdCG4KnNjM+Uj/3DSw7rT9PJvdML9ARS4M2Uqco9POPw+Aj20KBumsEUd6FMVcYBXAA=="],
|
||||
@@ -1348,6 +1354,8 @@
|
||||
|
||||
"@smithy/md5-js": ["@smithy/md5-js@4.2.12", "", { "dependencies": { "@smithy/types": "^4.13.1", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-W/oIpHCpWU2+iAkfZYyGWE+qkpuf3vEXHLxQQDx9FPNZTTdnul0dZ2d/gUFrtQ5je1G2kp4cjG0/24YueG2LbQ=="],
|
||||
|
||||
"@smithy/middleware-compression": ["@smithy/middleware-compression@4.3.42", "", { "dependencies": { "@smithy/core": "^3.23.13", "@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-config-provider": "^4.2.2", "@smithy/util-middleware": "^4.2.12", "@smithy/util-utf8": "^4.2.2", "fflate": "0.8.1", "tslib": "^2.6.2" } }, "sha512-Ys2R8N7oZ3b6p063lhk7paRbX1F9Ju8a8Bsrw2nFfsG8iHYpgfW6ijd7hJKqRe+Wq9ABfcmX3luBlEd+B5/jVA=="],
|
||||
|
||||
"@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=="],
|
||||
@@ -4122,6 +4130,10 @@
|
||||
|
||||
"@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/middleware-compression/fflate": ["fflate@0.8.1", "", {}, "sha512-/exOvEuc+/iaUm105QIiOt4LpBdMTWsXxqR0HDF35vx3fmaKzw7354gTilCh5rkzEt8WYyG//ku3h3nRmd7CHQ=="],
|
||||
|
||||
"@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=="],
|
||||
@@ -4660,6 +4672,8 @@
|
||||
|
||||
"@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=="],
|
||||
@@ -5128,6 +5142,8 @@
|
||||
|
||||
"@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=="],
|
||||
|
||||
Reference in New Issue
Block a user