mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
fix(cloudwatch): use PutAlarmMuteRule for mute/unmute with duration window (#4621)
* fix(cloudwatch): use PutAlarmMuteRule for mute/unmute with duration window * fix(cloudwatch): drop AWS StartDate to avoid race with at() trigger
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { CloudWatchClient, DisableAlarmActionsCommand } from '@aws-sdk/client-cloudwatch'
|
||||
import { CloudWatchClient, PutAlarmMuteRuleCommand } from '@aws-sdk/client-cloudwatch'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
@@ -9,6 +9,26 @@ import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
|
||||
const logger = createLogger('CloudWatchMuteAlarm')
|
||||
|
||||
function toAtExpression(date: Date): string {
|
||||
const yyyy = date.getUTCFullYear()
|
||||
const mm = String(date.getUTCMonth() + 1).padStart(2, '0')
|
||||
const dd = String(date.getUTCDate()).padStart(2, '0')
|
||||
const hh = String(date.getUTCHours()).padStart(2, '0')
|
||||
const min = String(date.getUTCMinutes()).padStart(2, '0')
|
||||
return `at(${yyyy}-${mm}-${dd}T${hh}:${min})`
|
||||
}
|
||||
|
||||
function toIsoDuration(value: number, unit: 'minutes' | 'hours' | 'days'): string {
|
||||
switch (unit) {
|
||||
case 'minutes':
|
||||
return `PT${value}M`
|
||||
case 'hours':
|
||||
return `PT${value}H`
|
||||
case 'days':
|
||||
return `P${value}D`
|
||||
}
|
||||
}
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
try {
|
||||
const auth = await checkInternalAuth(request)
|
||||
@@ -23,7 +43,14 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
if (!parsed.success) return parsed.response
|
||||
const validatedData = parsed.data.body
|
||||
|
||||
logger.info(`Muting ${validatedData.alarmNames.length} CloudWatch alarm(s)`)
|
||||
const startDate =
|
||||
validatedData.startDate !== undefined ? new Date(validatedData.startDate * 1000) : new Date()
|
||||
const expression = toAtExpression(startDate)
|
||||
const duration = toIsoDuration(validatedData.durationValue, validatedData.durationUnit)
|
||||
|
||||
logger.info(
|
||||
`Creating CloudWatch alarm mute rule "${validatedData.muteRuleName}" for ${validatedData.alarmNames.length} alarm(s) (${expression}, duration ${duration})`
|
||||
)
|
||||
|
||||
const client = new CloudWatchClient({
|
||||
region: validatedData.region,
|
||||
@@ -34,19 +61,30 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
})
|
||||
|
||||
try {
|
||||
const command = new DisableAlarmActionsCommand({
|
||||
AlarmNames: validatedData.alarmNames,
|
||||
const command = new PutAlarmMuteRuleCommand({
|
||||
Name: validatedData.muteRuleName,
|
||||
...(validatedData.description && { Description: validatedData.description }),
|
||||
Rule: {
|
||||
Schedule: {
|
||||
Expression: expression,
|
||||
Duration: duration,
|
||||
},
|
||||
},
|
||||
MuteTargets: { AlarmNames: validatedData.alarmNames },
|
||||
})
|
||||
|
||||
await client.send(command)
|
||||
|
||||
logger.info(`Successfully muted ${validatedData.alarmNames.length} alarm(s)`)
|
||||
logger.info(`Successfully created mute rule "${validatedData.muteRuleName}"`)
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
output: {
|
||||
success: true,
|
||||
muteRuleName: validatedData.muteRuleName,
|
||||
alarmNames: validatedData.alarmNames,
|
||||
expression,
|
||||
duration,
|
||||
},
|
||||
})
|
||||
} finally {
|
||||
@@ -55,7 +93,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
} catch (error) {
|
||||
logger.error('MuteAlarm failed', { error: toError(error).message })
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to mute CloudWatch alarm: ${toError(error).message}` },
|
||||
{ error: `Failed to create CloudWatch alarm mute rule: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CloudWatchClient, EnableAlarmActionsCommand } from '@aws-sdk/client-cloudwatch'
|
||||
import { CloudWatchClient, DeleteAlarmMuteRuleCommand } from '@aws-sdk/client-cloudwatch'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
@@ -23,7 +23,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
if (!parsed.success) return parsed.response
|
||||
const validatedData = parsed.data.body
|
||||
|
||||
logger.info(`Unmuting ${validatedData.alarmNames.length} CloudWatch alarm(s)`)
|
||||
logger.info(`Deleting CloudWatch alarm mute rule "${validatedData.muteRuleName}"`)
|
||||
|
||||
const client = new CloudWatchClient({
|
||||
region: validatedData.region,
|
||||
@@ -34,19 +34,19 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
})
|
||||
|
||||
try {
|
||||
const command = new EnableAlarmActionsCommand({
|
||||
AlarmNames: validatedData.alarmNames,
|
||||
const command = new DeleteAlarmMuteRuleCommand({
|
||||
AlarmMuteRuleName: validatedData.muteRuleName,
|
||||
})
|
||||
|
||||
await client.send(command)
|
||||
|
||||
logger.info(`Successfully unmuted ${validatedData.alarmNames.length} alarm(s)`)
|
||||
logger.info(`Successfully deleted mute rule "${validatedData.muteRuleName}"`)
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
output: {
|
||||
success: true,
|
||||
alarmNames: validatedData.alarmNames,
|
||||
muteRuleName: validatedData.muteRuleName,
|
||||
},
|
||||
})
|
||||
} finally {
|
||||
@@ -55,7 +55,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
} catch (error) {
|
||||
logger.error('UnmuteAlarm failed', { error: toError(error).message })
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to unmute CloudWatch alarm: ${toError(error).message}` },
|
||||
{ error: `Failed to delete CloudWatch alarm mute rule: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -366,13 +366,59 @@ Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'describe_alarms' },
|
||||
},
|
||||
{
|
||||
id: 'muteRuleName',
|
||||
title: 'Mute Rule Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'my-mute-rule',
|
||||
condition: { field: 'operation', value: ['mute_alarm', 'unmute_alarm'] },
|
||||
required: { field: 'operation', value: ['mute_alarm', 'unmute_alarm'] },
|
||||
},
|
||||
{
|
||||
id: 'alarmNames',
|
||||
title: 'Alarm Names',
|
||||
type: 'short-input',
|
||||
placeholder: 'my-alarm-1, my-alarm-2',
|
||||
condition: { field: 'operation', value: ['mute_alarm', 'unmute_alarm'] },
|
||||
required: { field: 'operation', value: ['mute_alarm', 'unmute_alarm'] },
|
||||
condition: { field: 'operation', value: 'mute_alarm' },
|
||||
required: { field: 'operation', value: 'mute_alarm' },
|
||||
},
|
||||
{
|
||||
id: 'durationValue',
|
||||
title: 'Duration',
|
||||
type: 'short-input',
|
||||
placeholder: '1',
|
||||
condition: { field: 'operation', value: 'mute_alarm' },
|
||||
required: { field: 'operation', value: 'mute_alarm' },
|
||||
},
|
||||
{
|
||||
id: 'durationUnit',
|
||||
title: 'Duration Unit',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Minutes', id: 'minutes' },
|
||||
{ label: 'Hours', id: 'hours' },
|
||||
{ label: 'Days', id: 'days' },
|
||||
],
|
||||
value: () => 'hours',
|
||||
condition: { field: 'operation', value: 'mute_alarm' },
|
||||
required: { field: 'operation', value: 'mute_alarm' },
|
||||
},
|
||||
{
|
||||
id: 'muteDescription',
|
||||
title: 'Description',
|
||||
type: 'short-input',
|
||||
placeholder: 'Why these alarms are being muted',
|
||||
condition: { field: 'operation', value: 'mute_alarm' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'muteStartDate',
|
||||
title: 'Start Date',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., 1711900800',
|
||||
condition: { field: 'operation', value: 'mute_alarm' },
|
||||
mode: 'advanced',
|
||||
description: 'Unix epoch seconds. Defaults to now (mute starts immediately).',
|
||||
},
|
||||
{
|
||||
id: 'limit',
|
||||
@@ -633,8 +679,7 @@ Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
|
||||
...(parsedLimit !== undefined && { limit: parsedLimit }),
|
||||
}
|
||||
|
||||
case 'mute_alarm':
|
||||
case 'unmute_alarm': {
|
||||
case 'mute_alarm': {
|
||||
const alarmNames = rest.alarmNames
|
||||
if (!alarmNames) {
|
||||
throw new Error('Alarm names are required')
|
||||
@@ -652,11 +697,45 @@ Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
|
||||
throw new Error('At least one alarm name is required')
|
||||
}
|
||||
|
||||
const durationValueRaw = rest.durationValue
|
||||
const parsedDurationValue =
|
||||
typeof durationValueRaw === 'number'
|
||||
? durationValueRaw
|
||||
: Number.parseInt(String(durationValueRaw ?? ''), 10)
|
||||
if (!Number.isFinite(parsedDurationValue) || parsedDurationValue < 1) {
|
||||
throw new Error('Duration must be a positive integer')
|
||||
}
|
||||
|
||||
const startDateRaw = rest.muteStartDate
|
||||
const parsedStartDate =
|
||||
startDateRaw === undefined || startDateRaw === ''
|
||||
? undefined
|
||||
: typeof startDateRaw === 'number'
|
||||
? startDateRaw
|
||||
: Number.parseInt(String(startDateRaw), 10)
|
||||
if (parsedStartDate !== undefined && !Number.isFinite(parsedStartDate)) {
|
||||
throw new Error('Start date must be a Unix epoch in seconds')
|
||||
}
|
||||
|
||||
return {
|
||||
awsRegion,
|
||||
awsAccessKeyId,
|
||||
awsSecretAccessKey,
|
||||
muteRuleName: rest.muteRuleName,
|
||||
alarmNames: names,
|
||||
durationValue: parsedDurationValue,
|
||||
durationUnit: rest.durationUnit,
|
||||
...(rest.muteDescription && { description: rest.muteDescription }),
|
||||
...(parsedStartDate !== undefined && { startDate: parsedStartDate }),
|
||||
}
|
||||
}
|
||||
|
||||
case 'unmute_alarm': {
|
||||
return {
|
||||
awsRegion,
|
||||
awsAccessKeyId,
|
||||
awsSecretAccessKey,
|
||||
muteRuleName: rest.muteRuleName,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -700,7 +779,18 @@ Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
|
||||
description: 'Alarm state filter (OK, ALARM, INSUFFICIENT_DATA)',
|
||||
},
|
||||
alarmType: { type: 'string', description: 'Alarm type filter (MetricAlarm, CompositeAlarm)' },
|
||||
alarmNames: { type: 'string', description: 'Comma-separated alarm names to mute or unmute' },
|
||||
muteRuleName: { type: 'string', description: 'Unique name for the alarm mute rule' },
|
||||
alarmNames: { type: 'string', description: 'Comma-separated alarm names to mute' },
|
||||
durationValue: { type: 'number', description: 'Length of the mute window' },
|
||||
durationUnit: {
|
||||
type: 'string',
|
||||
description: 'Unit for durationValue: minutes, hours, or days',
|
||||
},
|
||||
muteDescription: { type: 'string', description: 'Description of the mute rule' },
|
||||
muteStartDate: {
|
||||
type: 'number',
|
||||
description: 'When the mute begins (Unix epoch seconds). Defaults to now.',
|
||||
},
|
||||
limit: { type: 'number', description: 'Maximum number of results' },
|
||||
},
|
||||
outputs: {
|
||||
@@ -746,7 +836,19 @@ Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
|
||||
},
|
||||
alarmNames: {
|
||||
type: 'array',
|
||||
description: 'Names of the alarms that were muted or unmuted',
|
||||
description: 'Names of the alarms targeted by the mute rule',
|
||||
},
|
||||
muteRuleName: {
|
||||
type: 'string',
|
||||
description: 'Name of the alarm mute rule that was created or deleted',
|
||||
},
|
||||
expression: {
|
||||
type: 'string',
|
||||
description: 'Schedule expression used by the mute rule',
|
||||
},
|
||||
duration: {
|
||||
type: 'string',
|
||||
description: 'ISO 8601 duration of the mute window',
|
||||
},
|
||||
success: {
|
||||
type: 'boolean',
|
||||
|
||||
@@ -7,26 +7,58 @@ import type {
|
||||
import { defineRouteContract } from '@/lib/api/contracts/types'
|
||||
import { validateAwsRegion } from '@/lib/core/security/input-validation'
|
||||
|
||||
const MuteAlarmSchema = z.object({
|
||||
region: z
|
||||
.string()
|
||||
.min(1, 'AWS region is required')
|
||||
.refine((v) => validateAwsRegion(v).isValid, {
|
||||
message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)',
|
||||
}),
|
||||
accessKeyId: z.string().min(1, 'AWS access key ID is required'),
|
||||
secretAccessKey: z.string().min(1, 'AWS secret access key is required'),
|
||||
alarmNames: z
|
||||
.array(z.string().min(1, 'Alarm name cannot be empty'))
|
||||
.min(1, 'At least one alarm name is required')
|
||||
.max(100, 'At most 100 alarm names are allowed per request'),
|
||||
})
|
||||
const MAX_MUTE_MINUTES = 15 * 24 * 60
|
||||
|
||||
const MuteAlarmSchema = z
|
||||
.object({
|
||||
region: z
|
||||
.string()
|
||||
.min(1, 'AWS region is required')
|
||||
.refine((v) => validateAwsRegion(v).isValid, {
|
||||
message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)',
|
||||
}),
|
||||
accessKeyId: z.string().min(1, 'AWS access key ID is required'),
|
||||
secretAccessKey: z.string().min(1, 'AWS secret access key is required'),
|
||||
muteRuleName: z
|
||||
.string()
|
||||
.min(1, 'muteRuleName cannot be empty')
|
||||
.max(255, 'muteRuleName must be at most 255 characters'),
|
||||
alarmNames: z
|
||||
.array(z.string().min(1, 'Alarm name cannot be empty').max(255))
|
||||
.min(1, 'At least one alarm name is required')
|
||||
.max(100, 'At most 100 alarm names are allowed per mute rule'),
|
||||
durationValue: z
|
||||
.number()
|
||||
.int('durationValue must be an integer')
|
||||
.min(1, 'durationValue must be at least 1'),
|
||||
durationUnit: z.enum(['minutes', 'hours', 'days']),
|
||||
description: z.string().max(1024).optional(),
|
||||
startDate: z
|
||||
.number()
|
||||
.int('startDate must be an integer')
|
||||
.min(0, 'startDate must be a non-negative Unix epoch in seconds')
|
||||
.optional(),
|
||||
})
|
||||
.superRefine((data, ctx) => {
|
||||
const minutesPerUnit = { minutes: 1, hours: 60, days: 1440 } as const
|
||||
const totalMinutes = data.durationValue * minutesPerUnit[data.durationUnit]
|
||||
if (totalMinutes > MAX_MUTE_MINUTES) {
|
||||
ctx.addIssue({
|
||||
code: 'custom',
|
||||
message: 'duration must be at most 15 days (CloudWatch mute rule limit)',
|
||||
path: ['durationValue'],
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const MuteAlarmResponseSchema = z.object({
|
||||
success: z.literal(true),
|
||||
output: z.object({
|
||||
success: z.literal(true),
|
||||
muteRuleName: z.string(),
|
||||
alarmNames: z.array(z.string()),
|
||||
expression: z.string(),
|
||||
duration: z.string(),
|
||||
}),
|
||||
})
|
||||
|
||||
|
||||
@@ -16,17 +16,17 @@ const UnmuteAlarmSchema = z.object({
|
||||
}),
|
||||
accessKeyId: z.string().min(1, 'AWS access key ID is required'),
|
||||
secretAccessKey: z.string().min(1, 'AWS secret access key is required'),
|
||||
alarmNames: z
|
||||
.array(z.string().min(1, 'Alarm name cannot be empty'))
|
||||
.min(1, 'At least one alarm name is required')
|
||||
.max(100, 'At most 100 alarm names are allowed per request'),
|
||||
muteRuleName: z
|
||||
.string()
|
||||
.min(1, 'muteRuleName cannot be empty')
|
||||
.max(255, 'muteRuleName must be at most 255 characters'),
|
||||
})
|
||||
|
||||
const UnmuteAlarmResponseSchema = z.object({
|
||||
success: z.literal(true),
|
||||
output: z.object({
|
||||
success: z.literal(true),
|
||||
alarmNames: z.array(z.string()),
|
||||
muteRuleName: z.string(),
|
||||
}),
|
||||
})
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import type { ToolConfig } from '@/tools/types'
|
||||
export const muteAlarmTool: ToolConfig<CloudWatchMuteAlarmParams, CloudWatchMuteAlarmResponse> = {
|
||||
id: 'cloudwatch_mute_alarm',
|
||||
name: 'CloudWatch Mute Alarm',
|
||||
description: 'Disable notification actions on one or more CloudWatch alarms',
|
||||
description: 'Create a CloudWatch alarm mute rule that suppresses alarms for a fixed duration',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
@@ -29,11 +29,42 @@ export const muteAlarmTool: ToolConfig<CloudWatchMuteAlarmParams, CloudWatchMute
|
||||
visibility: 'user-only',
|
||||
description: 'AWS secret access key',
|
||||
},
|
||||
muteRuleName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique name for the mute rule (used later to unmute)',
|
||||
},
|
||||
alarmNames: {
|
||||
type: 'array',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Names of the CloudWatch alarms to mute',
|
||||
description: 'Names of the CloudWatch alarms this mute rule targets',
|
||||
},
|
||||
durationValue: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'How long the mute lasts (paired with durationUnit)',
|
||||
},
|
||||
durationUnit: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unit for durationValue: minutes, hours, or days',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Optional description of why the alarms are being muted',
|
||||
},
|
||||
startDate: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'When the mute window begins as Unix epoch seconds. Defaults to now (mute starts immediately).',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -47,7 +78,12 @@ export const muteAlarmTool: ToolConfig<CloudWatchMuteAlarmParams, CloudWatchMute
|
||||
region: params.awsRegion,
|
||||
accessKeyId: params.awsAccessKeyId,
|
||||
secretAccessKey: params.awsSecretAccessKey,
|
||||
muteRuleName: params.muteRuleName,
|
||||
alarmNames: params.alarmNames,
|
||||
durationValue: params.durationValue,
|
||||
durationUnit: params.durationUnit,
|
||||
...(params.description && { description: params.description }),
|
||||
...(params.startDate !== undefined && { startDate: params.startDate }),
|
||||
}),
|
||||
},
|
||||
|
||||
@@ -55,7 +91,7 @@ export const muteAlarmTool: ToolConfig<CloudWatchMuteAlarmParams, CloudWatchMute
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || 'Failed to mute CloudWatch alarm')
|
||||
throw new Error(data.error || 'Failed to create CloudWatch alarm mute rule')
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -65,11 +101,14 @@ export const muteAlarmTool: ToolConfig<CloudWatchMuteAlarmParams, CloudWatchMute
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the alarms were muted successfully' },
|
||||
success: { type: 'boolean', description: 'Whether the mute rule was created successfully' },
|
||||
muteRuleName: { type: 'string', description: 'Name of the mute rule that was created' },
|
||||
alarmNames: {
|
||||
type: 'array',
|
||||
description: 'Names of the alarms that were muted',
|
||||
description: 'Names of the alarms this rule mutes',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
expression: { type: 'string', description: 'Schedule expression used by the mute rule' },
|
||||
duration: { type: 'string', description: 'ISO 8601 duration of the mute window' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -164,24 +164,34 @@ export interface CloudWatchPutMetricDataResponse extends ToolResponse {
|
||||
}
|
||||
}
|
||||
|
||||
export type CloudWatchMuteDurationUnit = 'minutes' | 'hours' | 'days'
|
||||
|
||||
export interface CloudWatchMuteAlarmParams extends CloudWatchConnectionConfig {
|
||||
muteRuleName: string
|
||||
alarmNames: string[]
|
||||
durationValue: number
|
||||
durationUnit: CloudWatchMuteDurationUnit
|
||||
description?: string
|
||||
startDate?: number
|
||||
}
|
||||
|
||||
export interface CloudWatchMuteAlarmResponse extends ToolResponse {
|
||||
output: {
|
||||
success: boolean
|
||||
muteRuleName: string
|
||||
alarmNames: string[]
|
||||
expression: string
|
||||
duration: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface CloudWatchUnmuteAlarmParams extends CloudWatchConnectionConfig {
|
||||
alarmNames: string[]
|
||||
muteRuleName: string
|
||||
}
|
||||
|
||||
export interface CloudWatchUnmuteAlarmResponse extends ToolResponse {
|
||||
output: {
|
||||
success: boolean
|
||||
alarmNames: string[]
|
||||
muteRuleName: string
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ export const unmuteAlarmTool: ToolConfig<
|
||||
> = {
|
||||
id: 'cloudwatch_unmute_alarm',
|
||||
name: 'CloudWatch Unmute Alarm',
|
||||
description: 'Re-enable notification actions on one or more CloudWatch alarms',
|
||||
description: 'Delete a CloudWatch alarm mute rule, restoring alarm notifications',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
@@ -32,11 +32,11 @@ export const unmuteAlarmTool: ToolConfig<
|
||||
visibility: 'user-only',
|
||||
description: 'AWS secret access key',
|
||||
},
|
||||
alarmNames: {
|
||||
type: 'array',
|
||||
muteRuleName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Names of the CloudWatch alarms to unmute',
|
||||
description: 'Name of the mute rule to delete',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -50,7 +50,7 @@ export const unmuteAlarmTool: ToolConfig<
|
||||
region: params.awsRegion,
|
||||
accessKeyId: params.awsAccessKeyId,
|
||||
secretAccessKey: params.awsSecretAccessKey,
|
||||
alarmNames: params.alarmNames,
|
||||
muteRuleName: params.muteRuleName,
|
||||
}),
|
||||
},
|
||||
|
||||
@@ -58,7 +58,7 @@ export const unmuteAlarmTool: ToolConfig<
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || 'Failed to unmute CloudWatch alarm')
|
||||
throw new Error(data.error || 'Failed to delete CloudWatch alarm mute rule')
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -68,11 +68,7 @@ export const unmuteAlarmTool: ToolConfig<
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the alarms were unmuted successfully' },
|
||||
alarmNames: {
|
||||
type: 'array',
|
||||
description: 'Names of the alarms that were unmuted',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
success: { type: 'boolean', description: 'Whether the mute rule was deleted successfully' },
|
||||
muteRuleName: { type: 'string', description: 'Name of the mute rule that was deleted' },
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user