chore: import upstream snapshot with attribution
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
import type {
|
||||
RootlyAcknowledgeAlertParams,
|
||||
RootlyAcknowledgeAlertResponse,
|
||||
} from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyAcknowledgeAlertTool: ToolConfig<
|
||||
RootlyAcknowledgeAlertParams,
|
||||
RootlyAcknowledgeAlertResponse
|
||||
> = {
|
||||
id: 'rootly_acknowledge_alert',
|
||||
name: 'Rootly Acknowledge Alert',
|
||||
description: 'Acknowledge an alert in Rootly.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
alertId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the alert to acknowledge',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.rootly.com/v1/alerts/${params.alertId.trim()}/acknowledge`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
body: () => ({ data: {} }),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { alert: {} as RootlyAcknowledgeAlertResponse['output']['alert'] },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const attrs = data.data?.attributes || {}
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
alert: {
|
||||
id: data.data?.id ?? null,
|
||||
shortId: attrs.short_id ?? null,
|
||||
summary: attrs.summary ?? '',
|
||||
description: attrs.description ?? null,
|
||||
source: attrs.source ?? null,
|
||||
status: attrs.status ?? null,
|
||||
externalId: attrs.external_id ?? null,
|
||||
externalUrl: attrs.external_url ?? null,
|
||||
deduplicationKey: attrs.deduplication_key ?? null,
|
||||
createdAt: attrs.created_at ?? '',
|
||||
updatedAt: attrs.updated_at ?? '',
|
||||
startedAt: attrs.started_at ?? null,
|
||||
endedAt: attrs.ended_at ?? null,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
alert: {
|
||||
type: 'object',
|
||||
description: 'The acknowledged alert',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique alert ID' },
|
||||
shortId: { type: 'string', description: 'Short alert ID' },
|
||||
summary: { type: 'string', description: 'Alert summary' },
|
||||
description: { type: 'string', description: 'Alert description' },
|
||||
source: { type: 'string', description: 'Alert source' },
|
||||
status: { type: 'string', description: 'Alert status' },
|
||||
externalId: { type: 'string', description: 'External ID' },
|
||||
externalUrl: { type: 'string', description: 'External URL' },
|
||||
deduplicationKey: { type: 'string', description: 'Deduplication key' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
startedAt: { type: 'string', description: 'Start date' },
|
||||
endedAt: { type: 'string', description: 'End date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
import type {
|
||||
RootlyAddIncidentEventParams,
|
||||
RootlyAddIncidentEventResponse,
|
||||
} from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyAddIncidentEventTool: ToolConfig<
|
||||
RootlyAddIncidentEventParams,
|
||||
RootlyAddIncidentEventResponse
|
||||
> = {
|
||||
id: 'rootly_add_incident_event',
|
||||
name: 'Rootly Add Incident Event',
|
||||
description: 'Add a timeline event to an existing incident in Rootly.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
incidentId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the incident to add the event to',
|
||||
},
|
||||
event: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The summary/description of the event',
|
||||
},
|
||||
visibility: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Event visibility (internal or external)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.rootly.com/v1/incidents/${params.incidentId.trim()}/events`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
body: (params) => {
|
||||
const attributes: Record<string, unknown> = {
|
||||
event: params.event,
|
||||
}
|
||||
if (params.visibility) attributes.visibility = params.visibility
|
||||
return { data: { type: 'incident_events', attributes } }
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
eventId: '',
|
||||
event: '',
|
||||
visibility: null,
|
||||
occurredAt: null,
|
||||
createdAt: '',
|
||||
updatedAt: '',
|
||||
},
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const attrs = data.data?.attributes || {}
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
eventId: data.data?.id ?? '',
|
||||
event: attrs.event ?? '',
|
||||
visibility: attrs.visibility ?? null,
|
||||
occurredAt: attrs.occurred_at ?? null,
|
||||
createdAt: attrs.created_at ?? '',
|
||||
updatedAt: attrs.updated_at ?? '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
eventId: {
|
||||
type: 'string',
|
||||
description: 'The ID of the created event',
|
||||
},
|
||||
event: {
|
||||
type: 'string',
|
||||
description: 'The event summary',
|
||||
},
|
||||
visibility: {
|
||||
type: 'string',
|
||||
description: 'Event visibility (internal or external)',
|
||||
},
|
||||
occurredAt: {
|
||||
type: 'string',
|
||||
description: 'When the event occurred',
|
||||
},
|
||||
createdAt: {
|
||||
type: 'string',
|
||||
description: 'Creation date',
|
||||
},
|
||||
updatedAt: {
|
||||
type: 'string',
|
||||
description: 'Last update date',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
import type { RootlyAddSubscribersParams, RootlyIncidentActionResponse } from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyAddSubscribersTool: ToolConfig<
|
||||
RootlyAddSubscribersParams,
|
||||
RootlyIncidentActionResponse
|
||||
> = {
|
||||
id: 'rootly_add_subscribers',
|
||||
name: 'Rootly Add Subscribers',
|
||||
description: 'Subscribe users to a Rootly incident so they receive updates.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
incidentId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the incident',
|
||||
},
|
||||
userIds: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated user IDs to subscribe (use List Users to find IDs)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.rootly.com/v1/incidents/${params.incidentId.trim()}/add_subscribers`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
body: (params) => ({
|
||||
data: {
|
||||
type: 'incidents',
|
||||
attributes: {
|
||||
user_ids: params.userIds.split(',').map((s: string) => s.trim()),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { incident: {} as RootlyIncidentActionResponse['output']['incident'] },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const attrs = data.data?.attributes || {}
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
incident: {
|
||||
id: data.data?.id ?? null,
|
||||
sequentialId: attrs.sequential_id ?? null,
|
||||
title: attrs.title ?? '',
|
||||
slug: attrs.slug ?? null,
|
||||
kind: attrs.kind ?? null,
|
||||
summary: attrs.summary ?? null,
|
||||
status: attrs.status ?? null,
|
||||
private: attrs.private ?? false,
|
||||
url: attrs.url ?? null,
|
||||
shortUrl: attrs.short_url ?? null,
|
||||
severityName: attrs.severity?.data?.attributes?.name ?? null,
|
||||
severityId: attrs.severity?.data?.id ?? null,
|
||||
createdAt: attrs.created_at ?? '',
|
||||
updatedAt: attrs.updated_at ?? '',
|
||||
startedAt: attrs.started_at ?? null,
|
||||
mitigatedAt: attrs.mitigated_at ?? null,
|
||||
resolvedAt: attrs.resolved_at ?? null,
|
||||
closedAt: attrs.closed_at ?? null,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
incident: {
|
||||
type: 'object',
|
||||
description: 'The incident after subscribers were added',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique incident ID' },
|
||||
sequentialId: { type: 'number', description: 'Sequential incident number' },
|
||||
title: { type: 'string', description: 'Incident title' },
|
||||
slug: { type: 'string', description: 'Incident slug' },
|
||||
kind: { type: 'string', description: 'Incident kind' },
|
||||
summary: { type: 'string', description: 'Incident summary' },
|
||||
status: { type: 'string', description: 'Incident status' },
|
||||
private: { type: 'boolean', description: 'Whether the incident is private' },
|
||||
url: { type: 'string', description: 'URL to the incident' },
|
||||
shortUrl: { type: 'string', description: 'Short URL to the incident' },
|
||||
severityName: { type: 'string', description: 'Severity name' },
|
||||
severityId: { type: 'string', description: 'Severity ID' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
startedAt: { type: 'string', description: 'Start date' },
|
||||
mitigatedAt: { type: 'string', description: 'Mitigation date' },
|
||||
resolvedAt: { type: 'string', description: 'Resolution date' },
|
||||
closedAt: { type: 'string', description: 'Closed date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
import type {
|
||||
RootlyAssignIncidentRoleParams,
|
||||
RootlyIncidentActionResponse,
|
||||
} from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyAssignIncidentRoleTool: ToolConfig<
|
||||
RootlyAssignIncidentRoleParams,
|
||||
RootlyIncidentActionResponse
|
||||
> = {
|
||||
id: 'rootly_assign_incident_role',
|
||||
name: 'Rootly Assign Incident Role',
|
||||
description: 'Assign an incident role (e.g. commander) to a user on a Rootly incident.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
incidentId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the incident',
|
||||
},
|
||||
userId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the user to assign (use List Users to find IDs)',
|
||||
},
|
||||
incidentRoleId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the incident role (use List Incident Roles to find IDs)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.rootly.com/v1/incidents/${params.incidentId.trim()}/assign_role_to_user`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
body: (params) => ({
|
||||
data: {
|
||||
type: 'incidents',
|
||||
attributes: {
|
||||
user_id: params.userId.trim(),
|
||||
incident_role_id: params.incidentRoleId.trim(),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { incident: {} as RootlyIncidentActionResponse['output']['incident'] },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const attrs = data.data?.attributes || {}
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
incident: {
|
||||
id: data.data?.id ?? null,
|
||||
sequentialId: attrs.sequential_id ?? null,
|
||||
title: attrs.title ?? '',
|
||||
slug: attrs.slug ?? null,
|
||||
kind: attrs.kind ?? null,
|
||||
summary: attrs.summary ?? null,
|
||||
status: attrs.status ?? null,
|
||||
private: attrs.private ?? false,
|
||||
url: attrs.url ?? null,
|
||||
shortUrl: attrs.short_url ?? null,
|
||||
severityName: attrs.severity?.data?.attributes?.name ?? null,
|
||||
severityId: attrs.severity?.data?.id ?? null,
|
||||
createdAt: attrs.created_at ?? '',
|
||||
updatedAt: attrs.updated_at ?? '',
|
||||
startedAt: attrs.started_at ?? null,
|
||||
mitigatedAt: attrs.mitigated_at ?? null,
|
||||
resolvedAt: attrs.resolved_at ?? null,
|
||||
closedAt: attrs.closed_at ?? null,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
incident: {
|
||||
type: 'object',
|
||||
description: 'The incident after the role assignment',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique incident ID' },
|
||||
sequentialId: { type: 'number', description: 'Sequential incident number' },
|
||||
title: { type: 'string', description: 'Incident title' },
|
||||
slug: { type: 'string', description: 'Incident slug' },
|
||||
kind: { type: 'string', description: 'Incident kind' },
|
||||
summary: { type: 'string', description: 'Incident summary' },
|
||||
status: { type: 'string', description: 'Incident status' },
|
||||
private: { type: 'boolean', description: 'Whether the incident is private' },
|
||||
url: { type: 'string', description: 'URL to the incident' },
|
||||
shortUrl: { type: 'string', description: 'Short URL to the incident' },
|
||||
severityName: { type: 'string', description: 'Severity name' },
|
||||
severityId: { type: 'string', description: 'Severity ID' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
startedAt: { type: 'string', description: 'Start date' },
|
||||
mitigatedAt: { type: 'string', description: 'Mitigation date' },
|
||||
resolvedAt: { type: 'string', description: 'Resolution date' },
|
||||
closedAt: { type: 'string', description: 'Closed date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
import type {
|
||||
RootlyCreateActionItemParams,
|
||||
RootlyCreateActionItemResponse,
|
||||
} from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyCreateActionItemTool: ToolConfig<
|
||||
RootlyCreateActionItemParams,
|
||||
RootlyCreateActionItemResponse
|
||||
> = {
|
||||
id: 'rootly_create_action_item',
|
||||
name: 'Rootly Create Action Item',
|
||||
description: 'Create a new action item for an incident in Rootly.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
incidentId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the incident to add the action item to',
|
||||
},
|
||||
summary: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The title of the action item',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'A detailed description of the action item',
|
||||
},
|
||||
kind: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The kind of action item (task, follow_up)',
|
||||
},
|
||||
priority: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Priority level (high, medium, low)',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Action item status (open, in_progress, cancelled, done)',
|
||||
},
|
||||
assignedToUserId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The user ID to assign the action item to',
|
||||
},
|
||||
dueDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Due date for the action item',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.rootly.com/v1/incidents/${params.incidentId.trim()}/action_items`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
body: (params) => {
|
||||
const attributes: Record<string, unknown> = {
|
||||
summary: params.summary,
|
||||
}
|
||||
if (params.description) attributes.description = params.description
|
||||
if (params.kind) attributes.kind = params.kind
|
||||
if (params.priority) attributes.priority = params.priority
|
||||
if (params.status) attributes.status = params.status
|
||||
if (params.assignedToUserId) {
|
||||
const numericId = Number.parseInt(params.assignedToUserId, 10)
|
||||
if (!Number.isNaN(numericId)) attributes.assigned_to_user_id = numericId
|
||||
}
|
||||
if (params.dueDate) attributes.due_date = params.dueDate
|
||||
return { data: { type: 'incident_action_items', attributes } }
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { actionItem: {} as RootlyCreateActionItemResponse['output']['actionItem'] },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const attrs = data.data?.attributes || {}
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
actionItem: {
|
||||
id: data.data?.id ?? null,
|
||||
summary: attrs.summary ?? '',
|
||||
description: attrs.description ?? null,
|
||||
kind: attrs.kind ?? null,
|
||||
priority: attrs.priority ?? null,
|
||||
status: attrs.status ?? null,
|
||||
dueDate: attrs.due_date ?? null,
|
||||
createdAt: attrs.created_at ?? '',
|
||||
updatedAt: attrs.updated_at ?? '',
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
actionItem: {
|
||||
type: 'object',
|
||||
description: 'The created action item',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique action item ID' },
|
||||
summary: { type: 'string', description: 'Action item title' },
|
||||
description: { type: 'string', description: 'Action item description' },
|
||||
kind: { type: 'string', description: 'Action item kind (task, follow_up)' },
|
||||
priority: { type: 'string', description: 'Priority level' },
|
||||
status: { type: 'string', description: 'Action item status' },
|
||||
dueDate: { type: 'string', description: 'Due date' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
import type { RootlyCreateAlertParams, RootlyCreateAlertResponse } from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyCreateAlertTool: ToolConfig<RootlyCreateAlertParams, RootlyCreateAlertResponse> =
|
||||
{
|
||||
id: 'rootly_create_alert',
|
||||
name: 'Rootly Create Alert',
|
||||
description: 'Create a new alert in Rootly for on-call notification and routing.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
summary: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The summary of the alert',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'A detailed description of the alert',
|
||||
},
|
||||
source: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The source of the alert (e.g., api, manual, datadog, pagerduty)',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Alert status on creation (open, triggered)',
|
||||
},
|
||||
serviceIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated service IDs to attach',
|
||||
},
|
||||
groupIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated team/group IDs to attach',
|
||||
},
|
||||
environmentIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated environment IDs to attach',
|
||||
},
|
||||
externalId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'External ID for the alert',
|
||||
},
|
||||
externalUrl: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'External URL for the alert',
|
||||
},
|
||||
deduplicationKey: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Alerts sharing the same deduplication key are treated as a single alert',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.rootly.com/v1/alerts',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
body: (params) => {
|
||||
const attributes: Record<string, unknown> = {
|
||||
summary: params.summary,
|
||||
}
|
||||
if (params.source) attributes.source = params.source
|
||||
if (params.description) attributes.description = params.description
|
||||
if (params.status) attributes.status = params.status
|
||||
if (params.externalId) attributes.external_id = params.externalId
|
||||
if (params.externalUrl) attributes.external_url = params.externalUrl
|
||||
if (params.deduplicationKey) attributes.deduplication_key = params.deduplicationKey
|
||||
if (params.serviceIds) {
|
||||
attributes.service_ids = params.serviceIds.split(',').map((s: string) => s.trim())
|
||||
}
|
||||
if (params.groupIds) {
|
||||
attributes.group_ids = params.groupIds.split(',').map((s: string) => s.trim())
|
||||
}
|
||||
if (params.environmentIds) {
|
||||
attributes.environment_ids = params.environmentIds.split(',').map((s: string) => s.trim())
|
||||
}
|
||||
return { data: { type: 'alerts', attributes } }
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { alert: {} as RootlyCreateAlertResponse['output']['alert'] },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const attrs = data.data?.attributes || {}
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
alert: {
|
||||
id: data.data?.id ?? null,
|
||||
shortId: attrs.short_id ?? null,
|
||||
summary: attrs.summary ?? '',
|
||||
description: attrs.description ?? null,
|
||||
source: attrs.source ?? null,
|
||||
status: attrs.status ?? null,
|
||||
externalId: attrs.external_id ?? null,
|
||||
externalUrl: attrs.external_url ?? null,
|
||||
deduplicationKey: attrs.deduplication_key ?? null,
|
||||
createdAt: attrs.created_at ?? '',
|
||||
updatedAt: attrs.updated_at ?? '',
|
||||
startedAt: attrs.started_at ?? null,
|
||||
endedAt: attrs.ended_at ?? null,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
alert: {
|
||||
type: 'object',
|
||||
description: 'The created alert',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique alert ID' },
|
||||
shortId: { type: 'string', description: 'Short alert ID' },
|
||||
summary: { type: 'string', description: 'Alert summary' },
|
||||
description: { type: 'string', description: 'Alert description' },
|
||||
source: { type: 'string', description: 'Alert source' },
|
||||
status: { type: 'string', description: 'Alert status' },
|
||||
externalId: { type: 'string', description: 'External ID' },
|
||||
externalUrl: { type: 'string', description: 'External URL' },
|
||||
deduplicationKey: { type: 'string', description: 'Deduplication key' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
startedAt: { type: 'string', description: 'Start date' },
|
||||
endedAt: { type: 'string', description: 'End date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
import type { RootlyCreateIncidentParams, RootlyCreateIncidentResponse } from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyCreateIncidentTool: ToolConfig<
|
||||
RootlyCreateIncidentParams,
|
||||
RootlyCreateIncidentResponse
|
||||
> = {
|
||||
id: 'rootly_create_incident',
|
||||
name: 'Rootly Create Incident',
|
||||
description: 'Create a new incident in Rootly with optional severity, services, and teams.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The title of the incident (auto-generated if not provided)',
|
||||
},
|
||||
summary: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'A summary of the incident',
|
||||
},
|
||||
severityId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Severity ID to attach to the incident',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Incident status (in_triage, started, detected, acknowledged, mitigated, resolved, closed, cancelled, scheduled, in_progress, completed)',
|
||||
},
|
||||
kind: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Incident kind (normal, normal_sub, test, test_sub, example, example_sub, backfilled, scheduled, scheduled_sub)',
|
||||
},
|
||||
serviceIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated service IDs to attach',
|
||||
},
|
||||
environmentIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated environment IDs to attach',
|
||||
},
|
||||
groupIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated team/group IDs to attach',
|
||||
},
|
||||
incidentTypeIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated incident type IDs to attach',
|
||||
},
|
||||
functionalityIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated functionality IDs to attach',
|
||||
},
|
||||
labels: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Labels as JSON object, e.g. {"platform":"osx","version":"1.29"}',
|
||||
},
|
||||
private: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Create as a private incident (cannot be undone)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.rootly.com/v1/incidents',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
body: (params) => {
|
||||
const attributes: Record<string, unknown> = {}
|
||||
if (params.title) attributes.title = params.title
|
||||
if (params.summary) attributes.summary = params.summary
|
||||
if (params.severityId) attributes.severity_id = params.severityId
|
||||
if (params.status) attributes.status = params.status
|
||||
if (params.kind) attributes.kind = params.kind
|
||||
if (params.private !== undefined) attributes.private = params.private
|
||||
if (params.serviceIds) {
|
||||
attributes.service_ids = params.serviceIds.split(',').map((s: string) => s.trim())
|
||||
}
|
||||
if (params.environmentIds) {
|
||||
attributes.environment_ids = params.environmentIds.split(',').map((s: string) => s.trim())
|
||||
}
|
||||
if (params.groupIds) {
|
||||
attributes.group_ids = params.groupIds.split(',').map((s: string) => s.trim())
|
||||
}
|
||||
if (params.incidentTypeIds) {
|
||||
attributes.incident_type_ids = params.incidentTypeIds
|
||||
.split(',')
|
||||
.map((s: string) => s.trim())
|
||||
}
|
||||
if (params.functionalityIds) {
|
||||
attributes.functionality_ids = params.functionalityIds
|
||||
.split(',')
|
||||
.map((s: string) => s.trim())
|
||||
}
|
||||
if (params.labels) {
|
||||
try {
|
||||
attributes.labels = JSON.parse(params.labels)
|
||||
} catch {
|
||||
attributes.labels = params.labels
|
||||
}
|
||||
}
|
||||
return { data: { type: 'incidents', attributes } }
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
const errorMsg =
|
||||
errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`
|
||||
return {
|
||||
success: false,
|
||||
output: { incident: {} as RootlyCreateIncidentResponse['output']['incident'] },
|
||||
error: errorMsg,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const attrs = data.data?.attributes || {}
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
incident: {
|
||||
id: data.data?.id ?? null,
|
||||
sequentialId: attrs.sequential_id ?? null,
|
||||
title: attrs.title ?? '',
|
||||
slug: attrs.slug ?? null,
|
||||
kind: attrs.kind ?? null,
|
||||
summary: attrs.summary ?? null,
|
||||
status: attrs.status ?? null,
|
||||
private: attrs.private ?? false,
|
||||
url: attrs.url ?? null,
|
||||
shortUrl: attrs.short_url ?? null,
|
||||
severityName: attrs.severity?.data?.attributes?.name ?? null,
|
||||
severityId: attrs.severity?.data?.id ?? null,
|
||||
createdAt: attrs.created_at ?? '',
|
||||
updatedAt: attrs.updated_at ?? '',
|
||||
startedAt: attrs.started_at ?? null,
|
||||
mitigatedAt: attrs.mitigated_at ?? null,
|
||||
resolvedAt: attrs.resolved_at ?? null,
|
||||
closedAt: attrs.closed_at ?? null,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
incident: {
|
||||
type: 'object',
|
||||
description: 'The created incident',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique incident ID' },
|
||||
sequentialId: { type: 'number', description: 'Sequential incident number' },
|
||||
title: { type: 'string', description: 'Incident title' },
|
||||
slug: { type: 'string', description: 'Incident slug' },
|
||||
kind: { type: 'string', description: 'Incident kind' },
|
||||
summary: { type: 'string', description: 'Incident summary' },
|
||||
status: { type: 'string', description: 'Incident status' },
|
||||
private: { type: 'boolean', description: 'Whether the incident is private' },
|
||||
url: { type: 'string', description: 'URL to the incident' },
|
||||
shortUrl: { type: 'string', description: 'Short URL to the incident' },
|
||||
severityName: { type: 'string', description: 'Severity name' },
|
||||
severityId: { type: 'string', description: 'Severity ID' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
startedAt: { type: 'string', description: 'Start date' },
|
||||
mitigatedAt: { type: 'string', description: 'Mitigation date' },
|
||||
resolvedAt: { type: 'string', description: 'Resolution date' },
|
||||
closedAt: { type: 'string', description: 'Closed date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
import type {
|
||||
RootlyCreateStatusPageEventParams,
|
||||
RootlyCreateStatusPageEventResponse,
|
||||
} from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyCreateStatusPageEventTool: ToolConfig<
|
||||
RootlyCreateStatusPageEventParams,
|
||||
RootlyCreateStatusPageEventResponse
|
||||
> = {
|
||||
id: 'rootly_create_status_page_event',
|
||||
name: 'Rootly Create Status Page Event',
|
||||
description: 'Post a public status page update for a Rootly incident.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
incidentId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the incident',
|
||||
},
|
||||
event: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The status page update message to publish',
|
||||
},
|
||||
statusPageId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the status page to post to',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Status to set (investigating, identified, monitoring, resolved, scheduled, in_progress, completed)',
|
||||
},
|
||||
notifySubscribers: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to notify status page subscribers',
|
||||
},
|
||||
shouldTweet: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to post the update to the linked Twitter/X account',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.rootly.com/v1/incidents/${params.incidentId.trim()}/status-page-events`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
body: (params) => {
|
||||
const attributes: Record<string, unknown> = {
|
||||
event: params.event,
|
||||
}
|
||||
if (params.statusPageId) attributes.status_page_id = params.statusPageId.trim()
|
||||
if (params.status) attributes.status = params.status
|
||||
if (params.notifySubscribers !== undefined)
|
||||
attributes.notify_subscribers = params.notifySubscribers
|
||||
if (params.shouldTweet !== undefined) attributes.should_tweet = params.shouldTweet
|
||||
return { data: { type: 'incident_status_page_events', attributes } }
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
statusPageEvent: {} as RootlyCreateStatusPageEventResponse['output']['statusPageEvent'],
|
||||
},
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const attrs = data.data?.attributes || {}
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
statusPageEvent: {
|
||||
id: data.data?.id ?? null,
|
||||
event: attrs.event ?? '',
|
||||
statusPageId: attrs.status_page_id ?? null,
|
||||
status: attrs.status ?? null,
|
||||
notifySubscribers: attrs.notify_subscribers ?? null,
|
||||
shouldTweet: attrs.should_tweet ?? null,
|
||||
startedAt: attrs.started_at ?? null,
|
||||
createdAt: attrs.created_at ?? '',
|
||||
updatedAt: attrs.updated_at ?? '',
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
statusPageEvent: {
|
||||
type: 'object',
|
||||
description: 'The created status page event',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique status page event ID' },
|
||||
event: { type: 'string', description: 'The published update message' },
|
||||
statusPageId: { type: 'string', description: 'Status page ID' },
|
||||
status: { type: 'string', description: 'Status that was set' },
|
||||
notifySubscribers: { type: 'boolean', description: 'Whether subscribers were notified' },
|
||||
shouldTweet: { type: 'boolean', description: 'Whether the update was tweeted' },
|
||||
startedAt: { type: 'string', description: 'When the event started' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import type {
|
||||
RootlyDeleteActionItemParams,
|
||||
RootlyDeleteActionItemResponse,
|
||||
} from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyDeleteActionItemTool: ToolConfig<
|
||||
RootlyDeleteActionItemParams,
|
||||
RootlyDeleteActionItemResponse
|
||||
> = {
|
||||
id: 'rootly_delete_action_item',
|
||||
name: 'Rootly Delete Action Item',
|
||||
description: 'Delete a Rootly incident action item.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
actionItemId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the action item to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.rootly.com/v1/action_items/${params.actionItemId.trim()}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { success: false, message: '' },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
success: true,
|
||||
message: 'Action item deleted successfully',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the action item was deleted',
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
description: 'Result message',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import type { RootlyDeleteIncidentParams, RootlyDeleteIncidentResponse } from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyDeleteIncidentTool: ToolConfig<
|
||||
RootlyDeleteIncidentParams,
|
||||
RootlyDeleteIncidentResponse
|
||||
> = {
|
||||
id: 'rootly_delete_incident',
|
||||
name: 'Rootly Delete Incident',
|
||||
description: 'Delete an incident by ID from Rootly.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
incidentId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the incident to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.rootly.com/v1/incidents/${params.incidentId.trim()}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
success: false,
|
||||
message:
|
||||
errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
},
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
success: true,
|
||||
message: 'Incident deleted successfully',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the deletion succeeded' },
|
||||
message: { type: 'string', description: 'Result message' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
import type { RootlyAlertActionResponse, RootlyEscalateAlertParams } from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyEscalateAlertTool: ToolConfig<
|
||||
RootlyEscalateAlertParams,
|
||||
RootlyAlertActionResponse
|
||||
> = {
|
||||
id: 'rootly_escalate_alert',
|
||||
name: 'Rootly Escalate Alert',
|
||||
description: 'Escalate a Rootly alert, optionally to a specific escalation policy or level.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
alertId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the alert to escalate',
|
||||
},
|
||||
escalationPolicyId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Escalation policy ID to escalate to (use List Escalation Policies to find IDs)',
|
||||
},
|
||||
escalationPolicyLevel: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Escalation policy level to escalate to',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.rootly.com/v1/alerts/${params.alertId.trim()}/escalate`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
body: (params) => {
|
||||
const attributes: Record<string, unknown> = {}
|
||||
if (params.escalationPolicyId)
|
||||
attributes.escalation_policy_id = params.escalationPolicyId.trim()
|
||||
if (params.escalationPolicyLevel !== undefined)
|
||||
attributes.escalation_policy_level = params.escalationPolicyLevel
|
||||
if (Object.keys(attributes).length === 0) return { data: {} }
|
||||
return { data: { type: 'alerts', attributes } }
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { alert: {} as RootlyAlertActionResponse['output']['alert'] },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const attrs = data.data?.attributes || {}
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
alert: {
|
||||
id: data.data?.id ?? null,
|
||||
shortId: attrs.short_id ?? null,
|
||||
summary: attrs.summary ?? '',
|
||||
description: attrs.description ?? null,
|
||||
source: attrs.source ?? null,
|
||||
status: attrs.status ?? null,
|
||||
externalId: attrs.external_id ?? null,
|
||||
externalUrl: attrs.external_url ?? null,
|
||||
deduplicationKey: attrs.deduplication_key ?? null,
|
||||
createdAt: attrs.created_at ?? '',
|
||||
updatedAt: attrs.updated_at ?? '',
|
||||
startedAt: attrs.started_at ?? null,
|
||||
endedAt: attrs.ended_at ?? null,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
alert: {
|
||||
type: 'object',
|
||||
description: 'The escalated alert',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique alert ID' },
|
||||
shortId: { type: 'string', description: 'Short alert ID' },
|
||||
summary: { type: 'string', description: 'Alert summary' },
|
||||
description: { type: 'string', description: 'Alert description' },
|
||||
source: { type: 'string', description: 'Alert source' },
|
||||
status: { type: 'string', description: 'Alert status' },
|
||||
externalId: { type: 'string', description: 'External ID' },
|
||||
externalUrl: { type: 'string', description: 'External URL' },
|
||||
deduplicationKey: { type: 'string', description: 'Deduplication key' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
startedAt: { type: 'string', description: 'Start date' },
|
||||
endedAt: { type: 'string', description: 'End date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import type { RootlyGetAlertParams, RootlyGetAlertResponse } from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyGetAlertTool: ToolConfig<RootlyGetAlertParams, RootlyGetAlertResponse> = {
|
||||
id: 'rootly_get_alert',
|
||||
name: 'Rootly Get Alert',
|
||||
description: 'Retrieve a single alert by ID from Rootly.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
alertId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the alert to retrieve',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.rootly.com/v1/alerts/${params.alertId.trim()}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { alert: {} as RootlyGetAlertResponse['output']['alert'] },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const attrs = data.data?.attributes || {}
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
alert: {
|
||||
id: data.data?.id ?? null,
|
||||
shortId: attrs.short_id ?? null,
|
||||
summary: attrs.summary ?? '',
|
||||
description: attrs.description ?? null,
|
||||
source: attrs.source ?? null,
|
||||
status: attrs.status ?? null,
|
||||
externalId: attrs.external_id ?? null,
|
||||
externalUrl: attrs.external_url ?? null,
|
||||
deduplicationKey: attrs.deduplication_key ?? null,
|
||||
createdAt: attrs.created_at ?? '',
|
||||
updatedAt: attrs.updated_at ?? '',
|
||||
startedAt: attrs.started_at ?? null,
|
||||
endedAt: attrs.ended_at ?? null,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
alert: {
|
||||
type: 'object',
|
||||
description: 'The alert details',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique alert ID' },
|
||||
shortId: { type: 'string', description: 'Short alert ID' },
|
||||
summary: { type: 'string', description: 'Alert summary' },
|
||||
description: { type: 'string', description: 'Alert description' },
|
||||
source: { type: 'string', description: 'Alert source' },
|
||||
status: { type: 'string', description: 'Alert status' },
|
||||
externalId: { type: 'string', description: 'External ID' },
|
||||
externalUrl: { type: 'string', description: 'External URL' },
|
||||
deduplicationKey: { type: 'string', description: 'Deduplication key' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
startedAt: { type: 'string', description: 'Start date' },
|
||||
endedAt: { type: 'string', description: 'End date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
import type { RootlyGetIncidentParams, RootlyGetIncidentResponse } from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyGetIncidentTool: ToolConfig<RootlyGetIncidentParams, RootlyGetIncidentResponse> =
|
||||
{
|
||||
id: 'rootly_get_incident',
|
||||
name: 'Rootly Get Incident',
|
||||
description: 'Retrieve a single incident by ID from Rootly.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
incidentId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the incident to retrieve',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.rootly.com/v1/incidents/${params.incidentId.trim()}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { incident: {} as RootlyGetIncidentResponse['output']['incident'] },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const attrs = data.data?.attributes || {}
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
incident: {
|
||||
id: data.data?.id ?? null,
|
||||
sequentialId: attrs.sequential_id ?? null,
|
||||
title: attrs.title ?? '',
|
||||
slug: attrs.slug ?? null,
|
||||
kind: attrs.kind ?? null,
|
||||
summary: attrs.summary ?? null,
|
||||
status: attrs.status ?? null,
|
||||
private: attrs.private ?? false,
|
||||
url: attrs.url ?? null,
|
||||
shortUrl: attrs.short_url ?? null,
|
||||
severityName: attrs.severity?.data?.attributes?.name ?? null,
|
||||
severityId: attrs.severity?.data?.id ?? null,
|
||||
createdAt: attrs.created_at ?? '',
|
||||
updatedAt: attrs.updated_at ?? '',
|
||||
startedAt: attrs.started_at ?? null,
|
||||
mitigatedAt: attrs.mitigated_at ?? null,
|
||||
resolvedAt: attrs.resolved_at ?? null,
|
||||
closedAt: attrs.closed_at ?? null,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
incident: {
|
||||
type: 'object',
|
||||
description: 'The incident details',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique incident ID' },
|
||||
sequentialId: { type: 'number', description: 'Sequential incident number' },
|
||||
title: { type: 'string', description: 'Incident title' },
|
||||
slug: { type: 'string', description: 'Incident slug' },
|
||||
kind: { type: 'string', description: 'Incident kind' },
|
||||
summary: { type: 'string', description: 'Incident summary' },
|
||||
status: { type: 'string', description: 'Incident status' },
|
||||
private: { type: 'boolean', description: 'Whether the incident is private' },
|
||||
url: { type: 'string', description: 'URL to the incident' },
|
||||
shortUrl: { type: 'string', description: 'Short URL to the incident' },
|
||||
severityName: { type: 'string', description: 'Severity name' },
|
||||
severityId: { type: 'string', description: 'Severity ID' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
startedAt: { type: 'string', description: 'Start date' },
|
||||
mitigatedAt: { type: 'string', description: 'Mitigation date' },
|
||||
resolvedAt: { type: 'string', description: 'Resolution date' },
|
||||
closedAt: { type: 'string', description: 'Closed date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
export { rootlyAcknowledgeAlertTool } from '@/tools/rootly/acknowledge_alert'
|
||||
export { rootlyAddIncidentEventTool } from '@/tools/rootly/add_incident_event'
|
||||
export { rootlyAddSubscribersTool } from '@/tools/rootly/add_subscribers'
|
||||
export { rootlyAssignIncidentRoleTool } from '@/tools/rootly/assign_incident_role'
|
||||
export { rootlyCreateActionItemTool } from '@/tools/rootly/create_action_item'
|
||||
export { rootlyCreateAlertTool } from '@/tools/rootly/create_alert'
|
||||
export { rootlyCreateIncidentTool } from '@/tools/rootly/create_incident'
|
||||
export { rootlyCreateStatusPageEventTool } from '@/tools/rootly/create_status_page_event'
|
||||
export { rootlyDeleteActionItemTool } from '@/tools/rootly/delete_action_item'
|
||||
export { rootlyDeleteIncidentTool } from '@/tools/rootly/delete_incident'
|
||||
export { rootlyEscalateAlertTool } from '@/tools/rootly/escalate_alert'
|
||||
export { rootlyGetAlertTool } from '@/tools/rootly/get_alert'
|
||||
export { rootlyGetIncidentTool } from '@/tools/rootly/get_incident'
|
||||
export { rootlyListActionItemsTool } from '@/tools/rootly/list_action_items'
|
||||
export { rootlyListAlertsTool } from '@/tools/rootly/list_alerts'
|
||||
export { rootlyListCausesTool } from '@/tools/rootly/list_causes'
|
||||
export { rootlyListEnvironmentsTool } from '@/tools/rootly/list_environments'
|
||||
export { rootlyListEscalationPoliciesTool } from '@/tools/rootly/list_escalation_policies'
|
||||
export { rootlyListFunctionalitiesTool } from '@/tools/rootly/list_functionalities'
|
||||
export { rootlyListIncidentEventsTool } from '@/tools/rootly/list_incident_events'
|
||||
export { rootlyListIncidentRolesTool } from '@/tools/rootly/list_incident_roles'
|
||||
export { rootlyListIncidentTypesTool } from '@/tools/rootly/list_incident_types'
|
||||
export { rootlyListIncidentsTool } from '@/tools/rootly/list_incidents'
|
||||
export { rootlyListOnCallsTool } from '@/tools/rootly/list_on_calls'
|
||||
export { rootlyListPlaybooksTool } from '@/tools/rootly/list_playbooks'
|
||||
export { rootlyListRetrospectivesTool } from '@/tools/rootly/list_retrospectives'
|
||||
export { rootlyListSchedulesTool } from '@/tools/rootly/list_schedules'
|
||||
export { rootlyListServicesTool } from '@/tools/rootly/list_services'
|
||||
export { rootlyListSeveritiesTool } from '@/tools/rootly/list_severities'
|
||||
export { rootlyListTeamsTool } from '@/tools/rootly/list_teams'
|
||||
export { rootlyListUsersTool } from '@/tools/rootly/list_users'
|
||||
export { rootlyMitigateIncidentTool } from '@/tools/rootly/mitigate_incident'
|
||||
export { rootlyRemoveSubscribersTool } from '@/tools/rootly/remove_subscribers'
|
||||
export { rootlyResolveAlertTool } from '@/tools/rootly/resolve_alert'
|
||||
export { rootlyResolveIncidentTool } from '@/tools/rootly/resolve_incident'
|
||||
export { rootlyRunWorkflowTool } from '@/tools/rootly/run_workflow'
|
||||
export { rootlySnoozeAlertTool } from '@/tools/rootly/snooze_alert'
|
||||
export * from '@/tools/rootly/types'
|
||||
export { rootlyUnassignIncidentRoleTool } from '@/tools/rootly/unassign_incident_role'
|
||||
export { rootlyUpdateActionItemTool } from '@/tools/rootly/update_action_item'
|
||||
export { rootlyUpdateAlertTool } from '@/tools/rootly/update_alert'
|
||||
export { rootlyUpdateIncidentTool } from '@/tools/rootly/update_incident'
|
||||
@@ -0,0 +1,117 @@
|
||||
import type {
|
||||
RootlyListActionItemsParams,
|
||||
RootlyListActionItemsResponse,
|
||||
} from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyListActionItemsTool: ToolConfig<
|
||||
RootlyListActionItemsParams,
|
||||
RootlyListActionItemsResponse
|
||||
> = {
|
||||
id: 'rootly_list_action_items',
|
||||
name: 'Rootly List Action Items',
|
||||
description: 'List action items for an incident in Rootly.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
incidentId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the incident to list action items for',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of items per page (default: 20)',
|
||||
},
|
||||
pageNumber: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number for pagination',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
|
||||
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
|
||||
const qs = queryParams.toString()
|
||||
return `https://api.rootly.com/v1/incidents/${params.incidentId.trim()}/action_items${qs ? `?${qs}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { actionItems: [], totalCount: 0 },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const actionItems = (data.data || []).map((item: Record<string, unknown>) => {
|
||||
const attrs = (item.attributes || {}) as Record<string, unknown>
|
||||
return {
|
||||
id: item.id ?? null,
|
||||
summary: (attrs.summary as string) ?? '',
|
||||
description: (attrs.description as string) ?? null,
|
||||
kind: (attrs.kind as string) ?? null,
|
||||
priority: (attrs.priority as string) ?? null,
|
||||
status: (attrs.status as string) ?? null,
|
||||
dueDate: (attrs.due_date as string) ?? null,
|
||||
createdAt: (attrs.created_at as string) ?? '',
|
||||
updatedAt: (attrs.updated_at as string) ?? '',
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
actionItems,
|
||||
totalCount: data.meta?.total_count ?? actionItems.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
actionItems: {
|
||||
type: 'array',
|
||||
description: 'List of action items',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique action item ID' },
|
||||
summary: { type: 'string', description: 'Action item title' },
|
||||
description: { type: 'string', description: 'Action item description' },
|
||||
kind: { type: 'string', description: 'Action item kind (task, follow_up)' },
|
||||
priority: { type: 'string', description: 'Priority level' },
|
||||
status: { type: 'string', description: 'Action item status' },
|
||||
dueDate: { type: 'string', description: 'Due date' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
totalCount: {
|
||||
type: 'number',
|
||||
description: 'Total number of action items returned',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
import type { RootlyListAlertsParams, RootlyListAlertsResponse } from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyListAlertsTool: ToolConfig<RootlyListAlertsParams, RootlyListAlertsResponse> = {
|
||||
id: 'rootly_list_alerts',
|
||||
name: 'Rootly List Alerts',
|
||||
description: 'List alerts from Rootly with optional filtering by status, source, and services.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by status (open, triggered, acknowledged, resolved)',
|
||||
},
|
||||
source: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by source (e.g., api, datadog, pagerduty)',
|
||||
},
|
||||
services: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by service slugs (comma-separated)',
|
||||
},
|
||||
environments: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by environment slugs (comma-separated)',
|
||||
},
|
||||
groups: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by team/group slugs (comma-separated)',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of items per page (default: 20)',
|
||||
},
|
||||
pageNumber: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number for pagination',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.status) queryParams.set('filter[status]', params.status)
|
||||
if (params.source) queryParams.set('filter[source]', params.source)
|
||||
if (params.services) queryParams.set('filter[services]', params.services)
|
||||
if (params.environments) queryParams.set('filter[environments]', params.environments)
|
||||
if (params.groups) queryParams.set('filter[groups]', params.groups)
|
||||
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
|
||||
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
|
||||
const qs = queryParams.toString()
|
||||
return `https://api.rootly.com/v1/alerts${qs ? `?${qs}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { alerts: [], totalCount: 0 },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const alerts = (data.data || []).map((item: Record<string, unknown>) => {
|
||||
const attrs = (item.attributes || {}) as Record<string, unknown>
|
||||
return {
|
||||
id: item.id ?? null,
|
||||
shortId: (attrs.short_id as string) ?? null,
|
||||
summary: (attrs.summary as string) ?? '',
|
||||
description: (attrs.description as string) ?? null,
|
||||
source: (attrs.source as string) ?? null,
|
||||
status: (attrs.status as string) ?? null,
|
||||
externalId: (attrs.external_id as string) ?? null,
|
||||
externalUrl: (attrs.external_url as string) ?? null,
|
||||
deduplicationKey: (attrs.deduplication_key as string) ?? null,
|
||||
createdAt: (attrs.created_at as string) ?? '',
|
||||
updatedAt: (attrs.updated_at as string) ?? '',
|
||||
startedAt: (attrs.started_at as string) ?? null,
|
||||
endedAt: (attrs.ended_at as string) ?? null,
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
alerts,
|
||||
totalCount: data.meta?.total_count ?? alerts.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
alerts: {
|
||||
type: 'array',
|
||||
description: 'List of alerts',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique alert ID' },
|
||||
shortId: { type: 'string', description: 'Short alert ID' },
|
||||
summary: { type: 'string', description: 'Alert summary' },
|
||||
description: { type: 'string', description: 'Alert description' },
|
||||
source: { type: 'string', description: 'Alert source' },
|
||||
status: { type: 'string', description: 'Alert status' },
|
||||
externalId: { type: 'string', description: 'External ID' },
|
||||
externalUrl: { type: 'string', description: 'External URL' },
|
||||
deduplicationKey: { type: 'string', description: 'Deduplication key' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
startedAt: { type: 'string', description: 'Start date' },
|
||||
endedAt: { type: 'string', description: 'End date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
totalCount: {
|
||||
type: 'number',
|
||||
description: 'Total number of alerts returned',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
import type { RootlyListCausesParams, RootlyListCausesResponse } from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyListCausesTool: ToolConfig<RootlyListCausesParams, RootlyListCausesResponse> = {
|
||||
id: 'rootly_list_causes',
|
||||
name: 'Rootly List Causes',
|
||||
description: 'List causes from Rootly with optional search filtering.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
search: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Search term to filter causes',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of items per page (default: 20)',
|
||||
},
|
||||
pageNumber: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number for pagination',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.search) queryParams.set('filter[search]', params.search)
|
||||
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
|
||||
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
|
||||
const qs = queryParams.toString()
|
||||
return `https://api.rootly.com/v1/causes${qs ? `?${qs}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { causes: [], totalCount: 0 },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const causes = (data.data || []).map((item: Record<string, unknown>) => {
|
||||
const attrs = (item.attributes || {}) as Record<string, unknown>
|
||||
return {
|
||||
id: item.id ?? null,
|
||||
name: (attrs.name as string) ?? '',
|
||||
slug: (attrs.slug as string) ?? null,
|
||||
description: (attrs.description as string) ?? null,
|
||||
position: (attrs.position as number) ?? null,
|
||||
createdAt: (attrs.created_at as string) ?? '',
|
||||
updatedAt: (attrs.updated_at as string) ?? '',
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
causes,
|
||||
totalCount: data.meta?.total_count ?? causes.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
causes: {
|
||||
type: 'array',
|
||||
description: 'List of causes',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique cause ID' },
|
||||
name: { type: 'string', description: 'Cause name' },
|
||||
slug: { type: 'string', description: 'Cause slug' },
|
||||
description: { type: 'string', description: 'Cause description' },
|
||||
position: { type: 'number', description: 'Cause position' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
totalCount: {
|
||||
type: 'number',
|
||||
description: 'Total number of causes returned',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
import type {
|
||||
RootlyListEnvironmentsParams,
|
||||
RootlyListEnvironmentsResponse,
|
||||
} from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyListEnvironmentsTool: ToolConfig<
|
||||
RootlyListEnvironmentsParams,
|
||||
RootlyListEnvironmentsResponse
|
||||
> = {
|
||||
id: 'rootly_list_environments',
|
||||
name: 'Rootly List Environments',
|
||||
description: 'List environments configured in Rootly.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
search: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Search term to filter environments',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of items per page (default: 20)',
|
||||
},
|
||||
pageNumber: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number for pagination',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.search) queryParams.set('filter[search]', params.search)
|
||||
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
|
||||
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
|
||||
const qs = queryParams.toString()
|
||||
return `https://api.rootly.com/v1/environments${qs ? `?${qs}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { environments: [], totalCount: 0 },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const environments = (data.data || []).map((item: Record<string, unknown>) => {
|
||||
const attrs = (item.attributes || {}) as Record<string, unknown>
|
||||
return {
|
||||
id: item.id ?? null,
|
||||
name: (attrs.name as string) ?? '',
|
||||
slug: (attrs.slug as string) ?? null,
|
||||
description: (attrs.description as string) ?? null,
|
||||
color: (attrs.color as string) ?? null,
|
||||
createdAt: (attrs.created_at as string) ?? '',
|
||||
updatedAt: (attrs.updated_at as string) ?? '',
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
environments,
|
||||
totalCount: data.meta?.total_count ?? environments.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
environments: {
|
||||
type: 'array',
|
||||
description: 'List of environments',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique environment ID' },
|
||||
name: { type: 'string', description: 'Environment name' },
|
||||
slug: { type: 'string', description: 'Environment slug' },
|
||||
description: { type: 'string', description: 'Environment description' },
|
||||
color: { type: 'string', description: 'Environment color' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
totalCount: {
|
||||
type: 'number',
|
||||
description: 'Total number of environments returned',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
import type {
|
||||
RootlyListEscalationPoliciesParams,
|
||||
RootlyListEscalationPoliciesResponse,
|
||||
} from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyListEscalationPoliciesTool: ToolConfig<
|
||||
RootlyListEscalationPoliciesParams,
|
||||
RootlyListEscalationPoliciesResponse
|
||||
> = {
|
||||
id: 'rootly_list_escalation_policies',
|
||||
name: 'Rootly List Escalation Policies',
|
||||
description: 'List escalation policies from Rootly with optional search filtering.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
search: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Search term to filter escalation policies',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of items per page (default: 20)',
|
||||
},
|
||||
pageNumber: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number for pagination',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.search) queryParams.set('filter[search]', params.search)
|
||||
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
|
||||
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
|
||||
const qs = queryParams.toString()
|
||||
return `https://api.rootly.com/v1/escalation_policies${qs ? `?${qs}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { escalationPolicies: [], totalCount: 0 },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const escalationPolicies = (data.data || []).map((item: Record<string, unknown>) => {
|
||||
const attrs = (item.attributes || {}) as Record<string, unknown>
|
||||
return {
|
||||
id: item.id ?? null,
|
||||
name: (attrs.name as string) ?? '',
|
||||
description: (attrs.description as string) ?? null,
|
||||
repeatCount: (attrs.repeat_count as number) ?? null,
|
||||
groupIds: (attrs.group_ids as string[]) ?? null,
|
||||
serviceIds: (attrs.service_ids as string[]) ?? null,
|
||||
createdAt: (attrs.created_at as string) ?? '',
|
||||
updatedAt: (attrs.updated_at as string) ?? '',
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
escalationPolicies,
|
||||
totalCount: data.meta?.total_count ?? escalationPolicies.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
escalationPolicies: {
|
||||
type: 'array',
|
||||
description: 'List of escalation policies',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique escalation policy ID' },
|
||||
name: { type: 'string', description: 'Escalation policy name' },
|
||||
description: { type: 'string', description: 'Escalation policy description' },
|
||||
repeatCount: { type: 'number', description: 'Number of times to repeat escalation' },
|
||||
groupIds: { type: 'array', description: 'Associated group IDs' },
|
||||
serviceIds: { type: 'array', description: 'Associated service IDs' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
totalCount: {
|
||||
type: 'number',
|
||||
description: 'Total number of escalation policies returned',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
import type {
|
||||
RootlyListFunctionalitiesParams,
|
||||
RootlyListFunctionalitiesResponse,
|
||||
} from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyListFunctionalitiesTool: ToolConfig<
|
||||
RootlyListFunctionalitiesParams,
|
||||
RootlyListFunctionalitiesResponse
|
||||
> = {
|
||||
id: 'rootly_list_functionalities',
|
||||
name: 'Rootly List Functionalities',
|
||||
description: 'List functionalities configured in Rootly.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
search: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Search term to filter functionalities',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of items per page (default: 20)',
|
||||
},
|
||||
pageNumber: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number for pagination',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.search) queryParams.set('filter[search]', params.search)
|
||||
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
|
||||
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
|
||||
const qs = queryParams.toString()
|
||||
return `https://api.rootly.com/v1/functionalities${qs ? `?${qs}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { functionalities: [], totalCount: 0 },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const functionalities = (data.data || []).map((item: Record<string, unknown>) => {
|
||||
const attrs = (item.attributes || {}) as Record<string, unknown>
|
||||
return {
|
||||
id: item.id ?? null,
|
||||
name: (attrs.name as string) ?? '',
|
||||
slug: (attrs.slug as string) ?? null,
|
||||
description: (attrs.description as string) ?? null,
|
||||
color: (attrs.color as string) ?? null,
|
||||
createdAt: (attrs.created_at as string) ?? '',
|
||||
updatedAt: (attrs.updated_at as string) ?? '',
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
functionalities,
|
||||
totalCount: data.meta?.total_count ?? functionalities.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
functionalities: {
|
||||
type: 'array',
|
||||
description: 'List of functionalities',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique functionality ID' },
|
||||
name: { type: 'string', description: 'Functionality name' },
|
||||
slug: { type: 'string', description: 'Functionality slug' },
|
||||
description: { type: 'string', description: 'Functionality description' },
|
||||
color: { type: 'string', description: 'Functionality color' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
totalCount: {
|
||||
type: 'number',
|
||||
description: 'Total number of functionalities returned',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
import type {
|
||||
RootlyListIncidentEventsParams,
|
||||
RootlyListIncidentEventsResponse,
|
||||
} from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyListIncidentEventsTool: ToolConfig<
|
||||
RootlyListIncidentEventsParams,
|
||||
RootlyListIncidentEventsResponse
|
||||
> = {
|
||||
id: 'rootly_list_incident_events',
|
||||
name: 'Rootly List Incident Events',
|
||||
description: 'List the timeline events for a Rootly incident.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
incidentId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the incident',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of items per page (default: 20)',
|
||||
},
|
||||
pageNumber: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number for pagination',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
|
||||
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
|
||||
const qs = queryParams.toString()
|
||||
return `https://api.rootly.com/v1/incidents/${params.incidentId.trim()}/events${qs ? `?${qs}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { events: [], totalCount: 0 },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const events = (data.data || []).map((item: Record<string, unknown>) => {
|
||||
const attrs = (item.attributes || {}) as Record<string, unknown>
|
||||
return {
|
||||
id: item.id ?? null,
|
||||
event: (attrs.event as string) ?? '',
|
||||
visibility: (attrs.visibility as string) ?? null,
|
||||
occurredAt: (attrs.occurred_at as string) ?? null,
|
||||
createdAt: (attrs.created_at as string) ?? '',
|
||||
updatedAt: (attrs.updated_at as string) ?? '',
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
events,
|
||||
totalCount: data.meta?.total_count ?? events.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
events: {
|
||||
type: 'array',
|
||||
description: 'List of incident timeline events',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique event ID' },
|
||||
event: { type: 'string', description: 'The event description' },
|
||||
visibility: { type: 'string', description: 'Event visibility (internal or external)' },
|
||||
occurredAt: { type: 'string', description: 'When the event occurred' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
totalCount: {
|
||||
type: 'number',
|
||||
description: 'Total number of events returned',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
import type {
|
||||
RootlyListIncidentRolesParams,
|
||||
RootlyListIncidentRolesResponse,
|
||||
} from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyListIncidentRolesTool: ToolConfig<
|
||||
RootlyListIncidentRolesParams,
|
||||
RootlyListIncidentRolesResponse
|
||||
> = {
|
||||
id: 'rootly_list_incident_roles',
|
||||
name: 'Rootly List Incident Roles',
|
||||
description: 'List incident roles configured in Rootly (e.g. commander, scribe).',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
search: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Search term to filter incident roles',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of items per page (default: 20)',
|
||||
},
|
||||
pageNumber: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number for pagination',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.search) queryParams.set('filter[search]', params.search)
|
||||
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
|
||||
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
|
||||
const qs = queryParams.toString()
|
||||
return `https://api.rootly.com/v1/incident_roles${qs ? `?${qs}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { incidentRoles: [], totalCount: 0 },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const incidentRoles = (data.data || []).map((item: Record<string, unknown>) => {
|
||||
const attrs = (item.attributes || {}) as Record<string, unknown>
|
||||
return {
|
||||
id: item.id ?? null,
|
||||
name: (attrs.name as string) ?? '',
|
||||
slug: (attrs.slug as string) ?? null,
|
||||
summary: (attrs.summary as string) ?? null,
|
||||
description: (attrs.description as string) ?? null,
|
||||
position: (attrs.position as number) ?? null,
|
||||
optional: (attrs.optional as boolean) ?? null,
|
||||
enabled: (attrs.enabled as boolean) ?? null,
|
||||
createdAt: (attrs.created_at as string) ?? '',
|
||||
updatedAt: (attrs.updated_at as string) ?? '',
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
incidentRoles,
|
||||
totalCount: data.meta?.total_count ?? incidentRoles.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
incidentRoles: {
|
||||
type: 'array',
|
||||
description: 'List of incident roles',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique incident role ID' },
|
||||
name: { type: 'string', description: 'Role name' },
|
||||
slug: { type: 'string', description: 'Role slug' },
|
||||
summary: { type: 'string', description: 'Role summary' },
|
||||
description: { type: 'string', description: 'Role description' },
|
||||
position: { type: 'number', description: 'Display position' },
|
||||
optional: { type: 'boolean', description: 'Whether the role is optional' },
|
||||
enabled: { type: 'boolean', description: 'Whether the role is enabled' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
totalCount: {
|
||||
type: 'number',
|
||||
description: 'Total number of incident roles returned',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
import type {
|
||||
RootlyListIncidentTypesParams,
|
||||
RootlyListIncidentTypesResponse,
|
||||
} from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyListIncidentTypesTool: ToolConfig<
|
||||
RootlyListIncidentTypesParams,
|
||||
RootlyListIncidentTypesResponse
|
||||
> = {
|
||||
id: 'rootly_list_incident_types',
|
||||
name: 'Rootly List Incident Types',
|
||||
description: 'List incident types configured in Rootly.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
search: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter incident types by name',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of items per page (default: 20)',
|
||||
},
|
||||
pageNumber: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number for pagination',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.search) queryParams.set('filter[name]', params.search)
|
||||
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
|
||||
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
|
||||
const qs = queryParams.toString()
|
||||
return `https://api.rootly.com/v1/incident_types${qs ? `?${qs}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { incidentTypes: [], totalCount: 0 },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const incidentTypes = (data.data || []).map((item: Record<string, unknown>) => {
|
||||
const attrs = (item.attributes || {}) as Record<string, unknown>
|
||||
return {
|
||||
id: item.id ?? null,
|
||||
name: (attrs.name as string) ?? '',
|
||||
slug: (attrs.slug as string) ?? null,
|
||||
description: (attrs.description as string) ?? null,
|
||||
color: (attrs.color as string) ?? null,
|
||||
createdAt: (attrs.created_at as string) ?? '',
|
||||
updatedAt: (attrs.updated_at as string) ?? '',
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
incidentTypes,
|
||||
totalCount: data.meta?.total_count ?? incidentTypes.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
incidentTypes: {
|
||||
type: 'array',
|
||||
description: 'List of incident types',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique incident type ID' },
|
||||
name: { type: 'string', description: 'Incident type name' },
|
||||
slug: { type: 'string', description: 'Incident type slug' },
|
||||
description: { type: 'string', description: 'Incident type description' },
|
||||
color: { type: 'string', description: 'Incident type color' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
totalCount: {
|
||||
type: 'number',
|
||||
description: 'Total number of incident types returned',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
import type { RootlyListIncidentsParams, RootlyListIncidentsResponse } from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyListIncidentsTool: ToolConfig<
|
||||
RootlyListIncidentsParams,
|
||||
RootlyListIncidentsResponse
|
||||
> = {
|
||||
id: 'rootly_list_incidents',
|
||||
name: 'Rootly List Incidents',
|
||||
description: 'List incidents from Rootly with optional filtering by status, severity, and more.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Filter by status (in_triage, started, detected, acknowledged, mitigated, resolved, closed, cancelled, scheduled, in_progress, completed)',
|
||||
},
|
||||
severity: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by severity slug',
|
||||
},
|
||||
search: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Search term to filter incidents',
|
||||
},
|
||||
services: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by service slugs (comma-separated)',
|
||||
},
|
||||
teams: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by team slugs (comma-separated)',
|
||||
},
|
||||
environments: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by environment slugs (comma-separated)',
|
||||
},
|
||||
sort: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort order (e.g., -created_at, created_at, -started_at)',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of items per page (default: 20)',
|
||||
},
|
||||
pageNumber: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number for pagination',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.status) queryParams.set('filter[status]', params.status)
|
||||
if (params.severity) queryParams.set('filter[severity]', params.severity)
|
||||
if (params.search) queryParams.set('filter[search]', params.search)
|
||||
if (params.services) queryParams.set('filter[services]', params.services)
|
||||
if (params.teams) queryParams.set('filter[teams]', params.teams)
|
||||
if (params.environments) queryParams.set('filter[environments]', params.environments)
|
||||
if (params.sort) queryParams.set('sort', params.sort)
|
||||
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
|
||||
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
|
||||
const qs = queryParams.toString()
|
||||
return `https://api.rootly.com/v1/incidents${qs ? `?${qs}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { incidents: [], totalCount: 0 },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const incidents = (data.data || []).map((item: Record<string, unknown>) => {
|
||||
const attrs = (item.attributes || {}) as Record<string, unknown>
|
||||
const severity = attrs.severity as Record<string, unknown> | undefined
|
||||
const severityData = severity?.data as Record<string, unknown> | undefined
|
||||
const severityAttrs = severityData?.attributes as Record<string, unknown> | undefined
|
||||
return {
|
||||
id: item.id ?? null,
|
||||
sequentialId: (attrs.sequential_id as number) ?? null,
|
||||
title: (attrs.title as string) ?? '',
|
||||
slug: (attrs.slug as string) ?? null,
|
||||
kind: (attrs.kind as string) ?? null,
|
||||
summary: (attrs.summary as string) ?? null,
|
||||
status: (attrs.status as string) ?? null,
|
||||
private: (attrs.private as boolean) ?? false,
|
||||
url: (attrs.url as string) ?? null,
|
||||
shortUrl: (attrs.short_url as string) ?? null,
|
||||
severityName: (severityAttrs?.name as string) ?? null,
|
||||
severityId: (severityData?.id as string) ?? null,
|
||||
createdAt: (attrs.created_at as string) ?? '',
|
||||
updatedAt: (attrs.updated_at as string) ?? '',
|
||||
startedAt: (attrs.started_at as string) ?? null,
|
||||
mitigatedAt: (attrs.mitigated_at as string) ?? null,
|
||||
resolvedAt: (attrs.resolved_at as string) ?? null,
|
||||
closedAt: (attrs.closed_at as string) ?? null,
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
incidents,
|
||||
totalCount: data.meta?.total_count ?? incidents.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
incidents: {
|
||||
type: 'array',
|
||||
description: 'List of incidents',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique incident ID' },
|
||||
sequentialId: { type: 'number', description: 'Sequential incident number' },
|
||||
title: { type: 'string', description: 'Incident title' },
|
||||
slug: { type: 'string', description: 'Incident slug' },
|
||||
kind: { type: 'string', description: 'Incident kind' },
|
||||
summary: { type: 'string', description: 'Incident summary' },
|
||||
status: { type: 'string', description: 'Incident status' },
|
||||
private: { type: 'boolean', description: 'Whether the incident is private' },
|
||||
url: { type: 'string', description: 'URL to the incident' },
|
||||
shortUrl: { type: 'string', description: 'Short URL to the incident' },
|
||||
severityName: { type: 'string', description: 'Severity name' },
|
||||
severityId: { type: 'string', description: 'Severity ID' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
startedAt: { type: 'string', description: 'Start date' },
|
||||
mitigatedAt: { type: 'string', description: 'Mitigation date' },
|
||||
resolvedAt: { type: 'string', description: 'Resolution date' },
|
||||
closedAt: { type: 'string', description: 'Closed date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
totalCount: {
|
||||
type: 'number',
|
||||
description: 'Total number of incidents returned',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
import type { RootlyListOnCallsParams, RootlyListOnCallsResponse } from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyListOnCallsTool: ToolConfig<RootlyListOnCallsParams, RootlyListOnCallsResponse> =
|
||||
{
|
||||
id: 'rootly_list_on_calls',
|
||||
name: 'Rootly List On-Calls',
|
||||
description: 'List current on-call entries from Rootly with optional filtering.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
scheduleIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated schedule IDs to filter by',
|
||||
},
|
||||
escalationPolicyIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated escalation policy IDs to filter by',
|
||||
},
|
||||
userIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated user IDs to filter by',
|
||||
},
|
||||
serviceIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated service IDs to filter by',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.scheduleIds) queryParams.set('filter[schedule_ids]', params.scheduleIds)
|
||||
if (params.escalationPolicyIds)
|
||||
queryParams.set('filter[escalation_policy_ids]', params.escalationPolicyIds)
|
||||
if (params.userIds) queryParams.set('filter[user_ids]', params.userIds)
|
||||
if (params.serviceIds) queryParams.set('filter[service_ids]', params.serviceIds)
|
||||
queryParams.set('include', 'user,schedule,escalation_policy')
|
||||
return `https://api.rootly.com/v1/oncalls?${queryParams.toString()}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { onCalls: [], totalCount: 0 },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const included = (data.included || []) as Record<string, unknown>[]
|
||||
const findIncluded = (
|
||||
type: string,
|
||||
id: string | null
|
||||
): Record<string, unknown> | undefined =>
|
||||
id ? included.find((i) => i.type === type && i.id === id) : undefined
|
||||
|
||||
const onCalls = (data.data || []).map((item: Record<string, unknown>) => {
|
||||
const attrs = (item.attributes || {}) as Record<string, unknown>
|
||||
const rels = (item.relationships || {}) as Record<string, Record<string, unknown>>
|
||||
|
||||
const userId = ((rels.user?.data as Record<string, unknown>)?.id as string) ?? null
|
||||
const scheduleId = ((rels.schedule?.data as Record<string, unknown>)?.id as string) ?? null
|
||||
const escalationPolicyId =
|
||||
((rels.escalation_policy?.data as Record<string, unknown>)?.id as string) ?? null
|
||||
|
||||
const userIncl = findIncluded('users', userId)
|
||||
const scheduleIncl = findIncluded('schedules', scheduleId)
|
||||
|
||||
return {
|
||||
id: (item.id as string) ?? null,
|
||||
userId,
|
||||
userName: userIncl
|
||||
? (((userIncl.attributes as Record<string, unknown>)?.full_name as string) ?? null)
|
||||
: null,
|
||||
scheduleId,
|
||||
scheduleName: scheduleIncl
|
||||
? (((scheduleIncl.attributes as Record<string, unknown>)?.name as string) ?? null)
|
||||
: null,
|
||||
escalationPolicyId,
|
||||
startTime: (attrs.starts_at as string) ?? null,
|
||||
endTime: (attrs.ends_at as string) ?? null,
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
onCalls,
|
||||
totalCount: data.meta?.total_count ?? onCalls.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
onCalls: {
|
||||
type: 'array',
|
||||
description: 'List of on-call entries',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique on-call entry ID' },
|
||||
userId: { type: 'string', description: 'ID of the on-call user' },
|
||||
userName: { type: 'string', description: 'Name of the on-call user' },
|
||||
scheduleId: { type: 'string', description: 'ID of the associated schedule' },
|
||||
scheduleName: { type: 'string', description: 'Name of the associated schedule' },
|
||||
escalationPolicyId: {
|
||||
type: 'string',
|
||||
description: 'ID of the associated escalation policy',
|
||||
},
|
||||
startTime: { type: 'string', description: 'On-call start time' },
|
||||
endTime: { type: 'string', description: 'On-call end time' },
|
||||
},
|
||||
},
|
||||
},
|
||||
totalCount: {
|
||||
type: 'number',
|
||||
description: 'Total number of on-call entries returned',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import type { RootlyListPlaybooksParams, RootlyListPlaybooksResponse } from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyListPlaybooksTool: ToolConfig<
|
||||
RootlyListPlaybooksParams,
|
||||
RootlyListPlaybooksResponse
|
||||
> = {
|
||||
id: 'rootly_list_playbooks',
|
||||
name: 'Rootly List Playbooks',
|
||||
description: 'List playbooks from Rootly with pagination support.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of items per page (default: 20)',
|
||||
},
|
||||
pageNumber: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number for pagination',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
|
||||
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
|
||||
const qs = queryParams.toString()
|
||||
return `https://api.rootly.com/v1/playbooks${qs ? `?${qs}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { playbooks: [], totalCount: 0 },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const playbooks = (data.data || []).map((item: Record<string, unknown>) => {
|
||||
const attrs = (item.attributes || {}) as Record<string, unknown>
|
||||
return {
|
||||
id: item.id ?? null,
|
||||
title: (attrs.title as string) ?? '',
|
||||
summary: (attrs.summary as string) ?? null,
|
||||
externalUrl: (attrs.external_url as string) ?? null,
|
||||
createdAt: (attrs.created_at as string) ?? '',
|
||||
updatedAt: (attrs.updated_at as string) ?? '',
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
playbooks,
|
||||
totalCount: data.meta?.total_count ?? playbooks.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
playbooks: {
|
||||
type: 'array',
|
||||
description: 'List of playbooks',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique playbook ID' },
|
||||
title: { type: 'string', description: 'Playbook title' },
|
||||
summary: { type: 'string', description: 'Playbook summary' },
|
||||
externalUrl: { type: 'string', description: 'External URL' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
totalCount: {
|
||||
type: 'number',
|
||||
description: 'Total number of playbooks returned',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
import type {
|
||||
RootlyListRetrospectivesParams,
|
||||
RootlyListRetrospectivesResponse,
|
||||
} from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyListRetrospectivesTool: ToolConfig<
|
||||
RootlyListRetrospectivesParams,
|
||||
RootlyListRetrospectivesResponse
|
||||
> = {
|
||||
id: 'rootly_list_retrospectives',
|
||||
name: 'Rootly List Retrospectives',
|
||||
description: 'List incident retrospectives (post-mortems) from Rootly.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by status (draft, published)',
|
||||
},
|
||||
search: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Search term to filter retrospectives',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of items per page (default: 20)',
|
||||
},
|
||||
pageNumber: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number for pagination',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.status) queryParams.set('filter[status]', params.status)
|
||||
if (params.search) queryParams.set('filter[search]', params.search)
|
||||
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
|
||||
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
|
||||
const qs = queryParams.toString()
|
||||
return `https://api.rootly.com/v1/post_mortems${qs ? `?${qs}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { retrospectives: [], totalCount: 0 },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const retrospectives = (data.data || []).map((item: Record<string, unknown>) => {
|
||||
const attrs = (item.attributes || {}) as Record<string, unknown>
|
||||
return {
|
||||
id: item.id ?? null,
|
||||
title: (attrs.title as string) ?? '',
|
||||
status: (attrs.status as string) ?? null,
|
||||
url: (attrs.url as string) ?? null,
|
||||
startedAt: (attrs.started_at as string) ?? null,
|
||||
mitigatedAt: (attrs.mitigated_at as string) ?? null,
|
||||
resolvedAt: (attrs.resolved_at as string) ?? null,
|
||||
createdAt: (attrs.created_at as string) ?? '',
|
||||
updatedAt: (attrs.updated_at as string) ?? '',
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
retrospectives,
|
||||
totalCount: data.meta?.total_count ?? retrospectives.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
retrospectives: {
|
||||
type: 'array',
|
||||
description: 'List of retrospectives',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique retrospective ID' },
|
||||
title: { type: 'string', description: 'Retrospective title' },
|
||||
status: { type: 'string', description: 'Status (draft or published)' },
|
||||
url: { type: 'string', description: 'URL to the retrospective' },
|
||||
startedAt: { type: 'string', description: 'Incident start date' },
|
||||
mitigatedAt: { type: 'string', description: 'Mitigation date' },
|
||||
resolvedAt: { type: 'string', description: 'Resolution date' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
totalCount: {
|
||||
type: 'number',
|
||||
description: 'Total number of retrospectives returned',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
import type { RootlyListSchedulesParams, RootlyListSchedulesResponse } from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyListSchedulesTool: ToolConfig<
|
||||
RootlyListSchedulesParams,
|
||||
RootlyListSchedulesResponse
|
||||
> = {
|
||||
id: 'rootly_list_schedules',
|
||||
name: 'Rootly List Schedules',
|
||||
description: 'List on-call schedules from Rootly with optional search filtering.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
search: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Search term to filter schedules',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of items per page (default: 20)',
|
||||
},
|
||||
pageNumber: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number for pagination',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.search) queryParams.set('filter[search]', params.search)
|
||||
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
|
||||
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
|
||||
const qs = queryParams.toString()
|
||||
return `https://api.rootly.com/v1/schedules${qs ? `?${qs}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { schedules: [], totalCount: 0 },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const schedules = (data.data || []).map((item: Record<string, unknown>) => {
|
||||
const attrs = (item.attributes || {}) as Record<string, unknown>
|
||||
return {
|
||||
id: item.id ?? null,
|
||||
name: (attrs.name as string) ?? '',
|
||||
description: (attrs.description as string) ?? null,
|
||||
allTimeCoverage: (attrs.all_time_coverage as boolean) ?? null,
|
||||
createdAt: (attrs.created_at as string) ?? '',
|
||||
updatedAt: (attrs.updated_at as string) ?? '',
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
schedules,
|
||||
totalCount: data.meta?.total_count ?? schedules.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
schedules: {
|
||||
type: 'array',
|
||||
description: 'List of schedules',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique schedule ID' },
|
||||
name: { type: 'string', description: 'Schedule name' },
|
||||
description: { type: 'string', description: 'Schedule description' },
|
||||
allTimeCoverage: {
|
||||
type: 'boolean',
|
||||
description: 'Whether schedule provides 24/7 coverage',
|
||||
},
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
totalCount: {
|
||||
type: 'number',
|
||||
description: 'Total number of schedules returned',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
import type { RootlyListServicesParams, RootlyListServicesResponse } from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyListServicesTool: ToolConfig<
|
||||
RootlyListServicesParams,
|
||||
RootlyListServicesResponse
|
||||
> = {
|
||||
id: 'rootly_list_services',
|
||||
name: 'Rootly List Services',
|
||||
description: 'List services from Rootly with optional search filtering.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
search: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Search term to filter services',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of items per page (default: 20)',
|
||||
},
|
||||
pageNumber: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number for pagination',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.search) queryParams.set('filter[search]', params.search)
|
||||
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
|
||||
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
|
||||
const qs = queryParams.toString()
|
||||
return `https://api.rootly.com/v1/services${qs ? `?${qs}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { services: [], totalCount: 0 },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const services = (data.data || []).map((item: Record<string, unknown>) => {
|
||||
const attrs = (item.attributes || {}) as Record<string, unknown>
|
||||
return {
|
||||
id: item.id ?? null,
|
||||
name: (attrs.name as string) ?? '',
|
||||
slug: (attrs.slug as string) ?? null,
|
||||
description: (attrs.description as string) ?? null,
|
||||
color: (attrs.color as string) ?? null,
|
||||
createdAt: (attrs.created_at as string) ?? '',
|
||||
updatedAt: (attrs.updated_at as string) ?? '',
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
services,
|
||||
totalCount: data.meta?.total_count ?? services.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
services: {
|
||||
type: 'array',
|
||||
description: 'List of services',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique service ID' },
|
||||
name: { type: 'string', description: 'Service name' },
|
||||
slug: { type: 'string', description: 'Service slug' },
|
||||
description: { type: 'string', description: 'Service description' },
|
||||
color: { type: 'string', description: 'Service color' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
totalCount: {
|
||||
type: 'number',
|
||||
description: 'Total number of services returned',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
import type { RootlyListSeveritiesParams, RootlyListSeveritiesResponse } from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyListSeveritiesTool: ToolConfig<
|
||||
RootlyListSeveritiesParams,
|
||||
RootlyListSeveritiesResponse
|
||||
> = {
|
||||
id: 'rootly_list_severities',
|
||||
name: 'Rootly List Severities',
|
||||
description: 'List severity levels configured in Rootly.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
search: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Search term to filter severities',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of items per page (default: 20)',
|
||||
},
|
||||
pageNumber: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number for pagination',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.search) queryParams.set('filter[search]', params.search)
|
||||
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
|
||||
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
|
||||
const qs = queryParams.toString()
|
||||
return `https://api.rootly.com/v1/severities${qs ? `?${qs}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { severities: [], totalCount: 0 },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const severities = (data.data || []).map((item: Record<string, unknown>) => {
|
||||
const attrs = (item.attributes || {}) as Record<string, unknown>
|
||||
return {
|
||||
id: item.id ?? null,
|
||||
name: (attrs.name as string) ?? '',
|
||||
slug: (attrs.slug as string) ?? null,
|
||||
description: (attrs.description as string) ?? null,
|
||||
severity: (attrs.severity as string) ?? null,
|
||||
color: (attrs.color as string) ?? null,
|
||||
position: (attrs.position as number) ?? null,
|
||||
createdAt: (attrs.created_at as string) ?? '',
|
||||
updatedAt: (attrs.updated_at as string) ?? '',
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
severities,
|
||||
totalCount: data.meta?.total_count ?? severities.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
severities: {
|
||||
type: 'array',
|
||||
description: 'List of severity levels',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique severity ID' },
|
||||
name: { type: 'string', description: 'Severity name' },
|
||||
slug: { type: 'string', description: 'Severity slug' },
|
||||
description: { type: 'string', description: 'Severity description' },
|
||||
severity: { type: 'string', description: 'Severity level (critical, high, medium, low)' },
|
||||
color: { type: 'string', description: 'Severity color' },
|
||||
position: { type: 'number', description: 'Display position' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
totalCount: {
|
||||
type: 'number',
|
||||
description: 'Total number of severities returned',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
import type { RootlyListTeamsParams, RootlyListTeamsResponse } from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyListTeamsTool: ToolConfig<RootlyListTeamsParams, RootlyListTeamsResponse> = {
|
||||
id: 'rootly_list_teams',
|
||||
name: 'Rootly List Teams',
|
||||
description: 'List teams (groups) configured in Rootly.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
search: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Search term to filter teams',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of items per page (default: 20)',
|
||||
},
|
||||
pageNumber: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number for pagination',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.search) queryParams.set('filter[search]', params.search)
|
||||
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
|
||||
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
|
||||
const qs = queryParams.toString()
|
||||
return `https://api.rootly.com/v1/teams${qs ? `?${qs}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { teams: [], totalCount: 0 },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const teams = (data.data || []).map((item: Record<string, unknown>) => {
|
||||
const attrs = (item.attributes || {}) as Record<string, unknown>
|
||||
return {
|
||||
id: item.id ?? null,
|
||||
name: (attrs.name as string) ?? '',
|
||||
slug: (attrs.slug as string) ?? null,
|
||||
description: (attrs.description as string) ?? null,
|
||||
color: (attrs.color as string) ?? null,
|
||||
createdAt: (attrs.created_at as string) ?? '',
|
||||
updatedAt: (attrs.updated_at as string) ?? '',
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
teams,
|
||||
totalCount: data.meta?.total_count ?? teams.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
teams: {
|
||||
type: 'array',
|
||||
description: 'List of teams',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique team ID' },
|
||||
name: { type: 'string', description: 'Team name' },
|
||||
slug: { type: 'string', description: 'Team slug' },
|
||||
description: { type: 'string', description: 'Team description' },
|
||||
color: { type: 'string', description: 'Team color' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
totalCount: {
|
||||
type: 'number',
|
||||
description: 'Total number of teams returned',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
import type { RootlyListUsersParams, RootlyListUsersResponse } from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyListUsersTool: ToolConfig<RootlyListUsersParams, RootlyListUsersResponse> = {
|
||||
id: 'rootly_list_users',
|
||||
name: 'Rootly List Users',
|
||||
description: 'List users from Rootly with optional search and email filtering.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
search: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Search term to filter users',
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter users by email address',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of items per page (default: 20)',
|
||||
},
|
||||
pageNumber: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number for pagination',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.search) queryParams.set('filter[search]', params.search)
|
||||
if (params.email) queryParams.set('filter[email]', params.email)
|
||||
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
|
||||
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
|
||||
const qs = queryParams.toString()
|
||||
return `https://api.rootly.com/v1/users${qs ? `?${qs}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { users: [], totalCount: 0 },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const users = (data.data || []).map((item: Record<string, unknown>) => {
|
||||
const attrs = (item.attributes || {}) as Record<string, unknown>
|
||||
return {
|
||||
id: item.id ?? null,
|
||||
email: (attrs.email as string) ?? '',
|
||||
firstName: (attrs.first_name as string) ?? null,
|
||||
lastName: (attrs.last_name as string) ?? null,
|
||||
fullName: (attrs.full_name as string) ?? null,
|
||||
timeZone: (attrs.time_zone as string) ?? null,
|
||||
createdAt: (attrs.created_at as string) ?? '',
|
||||
updatedAt: (attrs.updated_at as string) ?? '',
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
users,
|
||||
totalCount: data.meta?.total_count ?? users.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
users: {
|
||||
type: 'array',
|
||||
description: 'List of users',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique user ID' },
|
||||
email: { type: 'string', description: 'User email address' },
|
||||
firstName: { type: 'string', description: 'User first name' },
|
||||
lastName: { type: 'string', description: 'User last name' },
|
||||
fullName: { type: 'string', description: 'User full name' },
|
||||
timeZone: { type: 'string', description: 'User time zone' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
totalCount: {
|
||||
type: 'number',
|
||||
description: 'Total number of users returned',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
import type {
|
||||
RootlyIncidentActionResponse,
|
||||
RootlyMitigateIncidentParams,
|
||||
} from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyMitigateIncidentTool: ToolConfig<
|
||||
RootlyMitigateIncidentParams,
|
||||
RootlyIncidentActionResponse
|
||||
> = {
|
||||
id: 'rootly_mitigate_incident',
|
||||
name: 'Rootly Mitigate Incident',
|
||||
description: 'Transition a Rootly incident to the mitigated state.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
incidentId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the incident to mitigate',
|
||||
},
|
||||
mitigationMessage: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'How was the incident mitigated?',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.rootly.com/v1/incidents/${params.incidentId.trim()}/mitigate`,
|
||||
method: 'PUT',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
body: (params) => {
|
||||
const attributes: Record<string, unknown> = {}
|
||||
if (params.mitigationMessage) attributes.mitigation_message = params.mitigationMessage
|
||||
return { data: { type: 'incidents', attributes } }
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { incident: {} as RootlyIncidentActionResponse['output']['incident'] },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const attrs = data.data?.attributes || {}
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
incident: {
|
||||
id: data.data?.id ?? null,
|
||||
sequentialId: attrs.sequential_id ?? null,
|
||||
title: attrs.title ?? '',
|
||||
slug: attrs.slug ?? null,
|
||||
kind: attrs.kind ?? null,
|
||||
summary: attrs.summary ?? null,
|
||||
status: attrs.status ?? null,
|
||||
private: attrs.private ?? false,
|
||||
url: attrs.url ?? null,
|
||||
shortUrl: attrs.short_url ?? null,
|
||||
severityName: attrs.severity?.data?.attributes?.name ?? null,
|
||||
severityId: attrs.severity?.data?.id ?? null,
|
||||
createdAt: attrs.created_at ?? '',
|
||||
updatedAt: attrs.updated_at ?? '',
|
||||
startedAt: attrs.started_at ?? null,
|
||||
mitigatedAt: attrs.mitigated_at ?? null,
|
||||
resolvedAt: attrs.resolved_at ?? null,
|
||||
closedAt: attrs.closed_at ?? null,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
incident: {
|
||||
type: 'object',
|
||||
description: 'The mitigated incident',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique incident ID' },
|
||||
sequentialId: { type: 'number', description: 'Sequential incident number' },
|
||||
title: { type: 'string', description: 'Incident title' },
|
||||
slug: { type: 'string', description: 'Incident slug' },
|
||||
kind: { type: 'string', description: 'Incident kind' },
|
||||
summary: { type: 'string', description: 'Incident summary' },
|
||||
status: { type: 'string', description: 'Incident status' },
|
||||
private: { type: 'boolean', description: 'Whether the incident is private' },
|
||||
url: { type: 'string', description: 'URL to the incident' },
|
||||
shortUrl: { type: 'string', description: 'Short URL to the incident' },
|
||||
severityName: { type: 'string', description: 'Severity name' },
|
||||
severityId: { type: 'string', description: 'Severity ID' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
startedAt: { type: 'string', description: 'Start date' },
|
||||
mitigatedAt: { type: 'string', description: 'Mitigation date' },
|
||||
resolvedAt: { type: 'string', description: 'Resolution date' },
|
||||
closedAt: { type: 'string', description: 'Closed date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
import type {
|
||||
RootlyIncidentActionResponse,
|
||||
RootlyRemoveSubscribersParams,
|
||||
} from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyRemoveSubscribersTool: ToolConfig<
|
||||
RootlyRemoveSubscribersParams,
|
||||
RootlyIncidentActionResponse
|
||||
> = {
|
||||
id: 'rootly_remove_subscribers',
|
||||
name: 'Rootly Remove Subscribers',
|
||||
description: 'Unsubscribe users from a Rootly incident.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
incidentId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the incident',
|
||||
},
|
||||
userIds: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated user IDs to unsubscribe (use List Users to find IDs)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.rootly.com/v1/incidents/${params.incidentId.trim()}/remove_subscribers`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
body: (params) => ({
|
||||
data: {
|
||||
type: 'incidents',
|
||||
attributes: {
|
||||
user_ids: params.userIds.split(',').map((s: string) => s.trim()),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { incident: {} as RootlyIncidentActionResponse['output']['incident'] },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const attrs = data.data?.attributes || {}
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
incident: {
|
||||
id: data.data?.id ?? null,
|
||||
sequentialId: attrs.sequential_id ?? null,
|
||||
title: attrs.title ?? '',
|
||||
slug: attrs.slug ?? null,
|
||||
kind: attrs.kind ?? null,
|
||||
summary: attrs.summary ?? null,
|
||||
status: attrs.status ?? null,
|
||||
private: attrs.private ?? false,
|
||||
url: attrs.url ?? null,
|
||||
shortUrl: attrs.short_url ?? null,
|
||||
severityName: attrs.severity?.data?.attributes?.name ?? null,
|
||||
severityId: attrs.severity?.data?.id ?? null,
|
||||
createdAt: attrs.created_at ?? '',
|
||||
updatedAt: attrs.updated_at ?? '',
|
||||
startedAt: attrs.started_at ?? null,
|
||||
mitigatedAt: attrs.mitigated_at ?? null,
|
||||
resolvedAt: attrs.resolved_at ?? null,
|
||||
closedAt: attrs.closed_at ?? null,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
incident: {
|
||||
type: 'object',
|
||||
description: 'The incident after subscribers were removed',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique incident ID' },
|
||||
sequentialId: { type: 'number', description: 'Sequential incident number' },
|
||||
title: { type: 'string', description: 'Incident title' },
|
||||
slug: { type: 'string', description: 'Incident slug' },
|
||||
kind: { type: 'string', description: 'Incident kind' },
|
||||
summary: { type: 'string', description: 'Incident summary' },
|
||||
status: { type: 'string', description: 'Incident status' },
|
||||
private: { type: 'boolean', description: 'Whether the incident is private' },
|
||||
url: { type: 'string', description: 'URL to the incident' },
|
||||
shortUrl: { type: 'string', description: 'Short URL to the incident' },
|
||||
severityName: { type: 'string', description: 'Severity name' },
|
||||
severityId: { type: 'string', description: 'Severity ID' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
startedAt: { type: 'string', description: 'Start date' },
|
||||
mitigatedAt: { type: 'string', description: 'Mitigation date' },
|
||||
resolvedAt: { type: 'string', description: 'Resolution date' },
|
||||
closedAt: { type: 'string', description: 'Closed date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
import type { RootlyResolveAlertParams, RootlyResolveAlertResponse } from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyResolveAlertTool: ToolConfig<
|
||||
RootlyResolveAlertParams,
|
||||
RootlyResolveAlertResponse
|
||||
> = {
|
||||
id: 'rootly_resolve_alert',
|
||||
name: 'Rootly Resolve Alert',
|
||||
description: 'Resolve an alert in Rootly.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
alertId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the alert to resolve',
|
||||
},
|
||||
resolutionMessage: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Message describing how the alert was resolved',
|
||||
},
|
||||
resolveRelatedIncidents: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to also resolve related incidents',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.rootly.com/v1/alerts/${params.alertId.trim()}/resolve`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
body: (params) => {
|
||||
const attributes: Record<string, unknown> = {}
|
||||
if (params.resolutionMessage) attributes.resolution_message = params.resolutionMessage
|
||||
if (params.resolveRelatedIncidents !== undefined)
|
||||
attributes.resolve_related_incidents = params.resolveRelatedIncidents
|
||||
if (Object.keys(attributes).length === 0) return { data: {} }
|
||||
return { data: { type: 'alerts', attributes } }
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { alert: {} as RootlyResolveAlertResponse['output']['alert'] },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const attrs = data.data?.attributes || {}
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
alert: {
|
||||
id: data.data?.id ?? null,
|
||||
shortId: attrs.short_id ?? null,
|
||||
summary: attrs.summary ?? '',
|
||||
description: attrs.description ?? null,
|
||||
source: attrs.source ?? null,
|
||||
status: attrs.status ?? null,
|
||||
externalId: attrs.external_id ?? null,
|
||||
externalUrl: attrs.external_url ?? null,
|
||||
deduplicationKey: attrs.deduplication_key ?? null,
|
||||
createdAt: attrs.created_at ?? '',
|
||||
updatedAt: attrs.updated_at ?? '',
|
||||
startedAt: attrs.started_at ?? null,
|
||||
endedAt: attrs.ended_at ?? null,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
alert: {
|
||||
type: 'object',
|
||||
description: 'The resolved alert',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique alert ID' },
|
||||
shortId: { type: 'string', description: 'Short alert ID' },
|
||||
summary: { type: 'string', description: 'Alert summary' },
|
||||
description: { type: 'string', description: 'Alert description' },
|
||||
source: { type: 'string', description: 'Alert source' },
|
||||
status: { type: 'string', description: 'Alert status' },
|
||||
externalId: { type: 'string', description: 'External ID' },
|
||||
externalUrl: { type: 'string', description: 'External URL' },
|
||||
deduplicationKey: { type: 'string', description: 'Deduplication key' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
startedAt: { type: 'string', description: 'Start date' },
|
||||
endedAt: { type: 'string', description: 'End date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
import type {
|
||||
RootlyIncidentActionResponse,
|
||||
RootlyResolveIncidentParams,
|
||||
} from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyResolveIncidentTool: ToolConfig<
|
||||
RootlyResolveIncidentParams,
|
||||
RootlyIncidentActionResponse
|
||||
> = {
|
||||
id: 'rootly_resolve_incident',
|
||||
name: 'Rootly Resolve Incident',
|
||||
description: 'Transition a Rootly incident to the resolved state.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
incidentId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the incident to resolve',
|
||||
},
|
||||
resolutionMessage: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'How was the incident resolved?',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.rootly.com/v1/incidents/${params.incidentId.trim()}/resolve`,
|
||||
method: 'PUT',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
body: (params) => {
|
||||
const attributes: Record<string, unknown> = {}
|
||||
if (params.resolutionMessage) attributes.resolution_message = params.resolutionMessage
|
||||
return { data: { type: 'incidents', attributes } }
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { incident: {} as RootlyIncidentActionResponse['output']['incident'] },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const attrs = data.data?.attributes || {}
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
incident: {
|
||||
id: data.data?.id ?? null,
|
||||
sequentialId: attrs.sequential_id ?? null,
|
||||
title: attrs.title ?? '',
|
||||
slug: attrs.slug ?? null,
|
||||
kind: attrs.kind ?? null,
|
||||
summary: attrs.summary ?? null,
|
||||
status: attrs.status ?? null,
|
||||
private: attrs.private ?? false,
|
||||
url: attrs.url ?? null,
|
||||
shortUrl: attrs.short_url ?? null,
|
||||
severityName: attrs.severity?.data?.attributes?.name ?? null,
|
||||
severityId: attrs.severity?.data?.id ?? null,
|
||||
createdAt: attrs.created_at ?? '',
|
||||
updatedAt: attrs.updated_at ?? '',
|
||||
startedAt: attrs.started_at ?? null,
|
||||
mitigatedAt: attrs.mitigated_at ?? null,
|
||||
resolvedAt: attrs.resolved_at ?? null,
|
||||
closedAt: attrs.closed_at ?? null,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
incident: {
|
||||
type: 'object',
|
||||
description: 'The resolved incident',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique incident ID' },
|
||||
sequentialId: { type: 'number', description: 'Sequential incident number' },
|
||||
title: { type: 'string', description: 'Incident title' },
|
||||
slug: { type: 'string', description: 'Incident slug' },
|
||||
kind: { type: 'string', description: 'Incident kind' },
|
||||
summary: { type: 'string', description: 'Incident summary' },
|
||||
status: { type: 'string', description: 'Incident status' },
|
||||
private: { type: 'boolean', description: 'Whether the incident is private' },
|
||||
url: { type: 'string', description: 'URL to the incident' },
|
||||
shortUrl: { type: 'string', description: 'Short URL to the incident' },
|
||||
severityName: { type: 'string', description: 'Severity name' },
|
||||
severityId: { type: 'string', description: 'Severity ID' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
startedAt: { type: 'string', description: 'Start date' },
|
||||
mitigatedAt: { type: 'string', description: 'Mitigation date' },
|
||||
resolvedAt: { type: 'string', description: 'Resolution date' },
|
||||
closedAt: { type: 'string', description: 'Closed date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
import type { RootlyRunWorkflowParams, RootlyRunWorkflowResponse } from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyRunWorkflowTool: ToolConfig<RootlyRunWorkflowParams, RootlyRunWorkflowResponse> =
|
||||
{
|
||||
id: 'rootly_run_workflow',
|
||||
name: 'Rootly Run Workflow',
|
||||
description: 'Trigger a Rootly automation workflow, optionally scoped to an incident or alert.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
workflowId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the workflow to run',
|
||||
},
|
||||
incidentId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Incident ID to run the workflow against',
|
||||
},
|
||||
alertId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Alert ID to run the workflow against',
|
||||
},
|
||||
immediate: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Run immediately (true) or respect the workflow wait time (false). Default true',
|
||||
},
|
||||
checkConditions: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to evaluate the workflow conditions before running. Default false',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.rootly.com/v1/workflows/${params.workflowId.trim()}/workflow_runs`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
body: (params) => {
|
||||
const attributes: Record<string, unknown> = {}
|
||||
if (params.incidentId) attributes.incident_id = params.incidentId.trim()
|
||||
if (params.alertId) attributes.alert_id = params.alertId.trim()
|
||||
if (params.immediate !== undefined) attributes.immediate = params.immediate
|
||||
if (params.checkConditions !== undefined)
|
||||
attributes.check_conditions = params.checkConditions
|
||||
return { data: { type: 'workflow_runs', attributes } }
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { workflowRun: {} as RootlyRunWorkflowResponse['output']['workflowRun'] },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const attrs = data.data?.attributes || {}
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
workflowRun: {
|
||||
id: data.data?.id ?? null,
|
||||
workflowId: attrs.workflow_id ?? null,
|
||||
status: attrs.status ?? null,
|
||||
statusMessage: attrs.status_message ?? null,
|
||||
triggeredBy: attrs.triggered_by ?? null,
|
||||
incidentId: attrs.incident_id ?? null,
|
||||
alertId: attrs.alert_id ?? null,
|
||||
startedAt: attrs.started_at ?? null,
|
||||
completedAt: attrs.completed_at ?? null,
|
||||
failedAt: attrs.failed_at ?? null,
|
||||
canceledAt: attrs.canceled_at ?? null,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
workflowRun: {
|
||||
type: 'object',
|
||||
description: 'The triggered workflow run',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique workflow run ID' },
|
||||
workflowId: { type: 'string', description: 'ID of the workflow that ran' },
|
||||
status: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Run status (queued, started, completed, completed_with_errors, failed, canceled)',
|
||||
},
|
||||
statusMessage: { type: 'string', description: 'Status detail message' },
|
||||
triggeredBy: {
|
||||
type: 'string',
|
||||
description: 'What triggered the run (system, user, workflow)',
|
||||
},
|
||||
incidentId: { type: 'string', description: 'Associated incident ID' },
|
||||
alertId: { type: 'string', description: 'Associated alert ID' },
|
||||
startedAt: { type: 'string', description: 'When the run started' },
|
||||
completedAt: { type: 'string', description: 'When the run completed' },
|
||||
failedAt: { type: 'string', description: 'When the run failed' },
|
||||
canceledAt: { type: 'string', description: 'When the run was canceled' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import type { RootlyAlertActionResponse, RootlySnoozeAlertParams } from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlySnoozeAlertTool: ToolConfig<RootlySnoozeAlertParams, RootlyAlertActionResponse> =
|
||||
{
|
||||
id: 'rootly_snooze_alert',
|
||||
name: 'Rootly Snooze Alert',
|
||||
description: 'Snooze a Rootly alert for a set number of minutes.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
alertId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the alert to snooze',
|
||||
},
|
||||
delayMinutes: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of minutes to snooze the alert',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.rootly.com/v1/alerts/${params.alertId.trim()}/snooze`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
body: (params) => ({
|
||||
data: {
|
||||
type: 'alerts',
|
||||
attributes: { delay_minutes: params.delayMinutes },
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { alert: {} as RootlyAlertActionResponse['output']['alert'] },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const attrs = data.data?.attributes || {}
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
alert: {
|
||||
id: data.data?.id ?? null,
|
||||
shortId: attrs.short_id ?? null,
|
||||
summary: attrs.summary ?? '',
|
||||
description: attrs.description ?? null,
|
||||
source: attrs.source ?? null,
|
||||
status: attrs.status ?? null,
|
||||
externalId: attrs.external_id ?? null,
|
||||
externalUrl: attrs.external_url ?? null,
|
||||
deduplicationKey: attrs.deduplication_key ?? null,
|
||||
createdAt: attrs.created_at ?? '',
|
||||
updatedAt: attrs.updated_at ?? '',
|
||||
startedAt: attrs.started_at ?? null,
|
||||
endedAt: attrs.ended_at ?? null,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
alert: {
|
||||
type: 'object',
|
||||
description: 'The snoozed alert',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique alert ID' },
|
||||
shortId: { type: 'string', description: 'Short alert ID' },
|
||||
summary: { type: 'string', description: 'Alert summary' },
|
||||
description: { type: 'string', description: 'Alert description' },
|
||||
source: { type: 'string', description: 'Alert source' },
|
||||
status: { type: 'string', description: 'Alert status' },
|
||||
externalId: { type: 'string', description: 'External ID' },
|
||||
externalUrl: { type: 'string', description: 'External URL' },
|
||||
deduplicationKey: { type: 'string', description: 'Deduplication key' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
startedAt: { type: 'string', description: 'Start date' },
|
||||
endedAt: { type: 'string', description: 'End date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,849 @@
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
/** Base parameters for all Rootly API operations */
|
||||
interface RootlyBaseParams {
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
/** Create Incident */
|
||||
export interface RootlyCreateIncidentParams extends RootlyBaseParams {
|
||||
title?: string
|
||||
summary?: string
|
||||
severityId?: string
|
||||
status?: string
|
||||
kind?: string
|
||||
serviceIds?: string
|
||||
environmentIds?: string
|
||||
groupIds?: string
|
||||
incidentTypeIds?: string
|
||||
functionalityIds?: string
|
||||
labels?: string
|
||||
private?: boolean
|
||||
}
|
||||
|
||||
interface RootlyIncidentData {
|
||||
id: string | null
|
||||
sequentialId: number | null
|
||||
title: string
|
||||
slug: string | null
|
||||
kind: string | null
|
||||
summary: string | null
|
||||
status: string | null
|
||||
private: boolean
|
||||
url: string | null
|
||||
shortUrl: string | null
|
||||
severityName: string | null
|
||||
severityId: string | null
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
startedAt: string | null
|
||||
mitigatedAt: string | null
|
||||
resolvedAt: string | null
|
||||
closedAt: string | null
|
||||
}
|
||||
|
||||
export interface RootlyCreateIncidentResponse extends ToolResponse {
|
||||
output: {
|
||||
incident: RootlyIncidentData
|
||||
}
|
||||
}
|
||||
|
||||
/** Get Incident */
|
||||
export interface RootlyGetIncidentParams extends RootlyBaseParams {
|
||||
incidentId: string
|
||||
}
|
||||
|
||||
export interface RootlyGetIncidentResponse extends ToolResponse {
|
||||
output: {
|
||||
incident: RootlyIncidentData
|
||||
}
|
||||
}
|
||||
|
||||
/** Update Incident */
|
||||
export interface RootlyUpdateIncidentParams extends RootlyBaseParams {
|
||||
incidentId: string
|
||||
title?: string
|
||||
summary?: string
|
||||
severityId?: string
|
||||
status?: string
|
||||
kind?: string
|
||||
private?: boolean
|
||||
serviceIds?: string
|
||||
environmentIds?: string
|
||||
groupIds?: string
|
||||
incidentTypeIds?: string
|
||||
functionalityIds?: string
|
||||
labels?: string
|
||||
mitigationMessage?: string
|
||||
resolutionMessage?: string
|
||||
cancellationMessage?: string
|
||||
}
|
||||
|
||||
export interface RootlyUpdateIncidentResponse extends ToolResponse {
|
||||
output: {
|
||||
incident: RootlyIncidentData
|
||||
}
|
||||
}
|
||||
|
||||
/** List Incidents */
|
||||
export interface RootlyListIncidentsParams extends RootlyBaseParams {
|
||||
status?: string
|
||||
severity?: string
|
||||
search?: string
|
||||
services?: string
|
||||
teams?: string
|
||||
environments?: string
|
||||
sort?: string
|
||||
pageSize?: number
|
||||
pageNumber?: number
|
||||
}
|
||||
|
||||
export interface RootlyListIncidentsResponse extends ToolResponse {
|
||||
output: {
|
||||
incidents: RootlyIncidentData[]
|
||||
totalCount: number
|
||||
}
|
||||
}
|
||||
|
||||
/** Create Alert */
|
||||
export interface RootlyCreateAlertParams extends RootlyBaseParams {
|
||||
summary: string
|
||||
source?: string
|
||||
description?: string
|
||||
status?: string
|
||||
serviceIds?: string
|
||||
groupIds?: string
|
||||
environmentIds?: string
|
||||
externalId?: string
|
||||
externalUrl?: string
|
||||
deduplicationKey?: string
|
||||
}
|
||||
|
||||
interface RootlyAlertData {
|
||||
id: string | null
|
||||
shortId: string | null
|
||||
summary: string
|
||||
description: string | null
|
||||
source: string | null
|
||||
status: string | null
|
||||
externalId: string | null
|
||||
externalUrl: string | null
|
||||
deduplicationKey: string | null
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
startedAt: string | null
|
||||
endedAt: string | null
|
||||
}
|
||||
|
||||
export interface RootlyCreateAlertResponse extends ToolResponse {
|
||||
output: {
|
||||
alert: RootlyAlertData
|
||||
}
|
||||
}
|
||||
|
||||
/** List Alerts */
|
||||
export interface RootlyListAlertsParams extends RootlyBaseParams {
|
||||
status?: string
|
||||
source?: string
|
||||
services?: string
|
||||
environments?: string
|
||||
groups?: string
|
||||
pageSize?: number
|
||||
pageNumber?: number
|
||||
}
|
||||
|
||||
export interface RootlyListAlertsResponse extends ToolResponse {
|
||||
output: {
|
||||
alerts: RootlyAlertData[]
|
||||
totalCount: number
|
||||
}
|
||||
}
|
||||
|
||||
/** Add Incident Event */
|
||||
export interface RootlyAddIncidentEventParams extends RootlyBaseParams {
|
||||
incidentId: string
|
||||
event: string
|
||||
visibility?: string
|
||||
}
|
||||
|
||||
export interface RootlyAddIncidentEventResponse extends ToolResponse {
|
||||
output: {
|
||||
eventId: string
|
||||
event: string
|
||||
visibility: string | null
|
||||
occurredAt: string | null
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
}
|
||||
|
||||
/** List Services */
|
||||
export interface RootlyListServicesParams extends RootlyBaseParams {
|
||||
search?: string
|
||||
pageSize?: number
|
||||
pageNumber?: number
|
||||
}
|
||||
|
||||
interface RootlyServiceData {
|
||||
id: string | null
|
||||
name: string
|
||||
slug: string | null
|
||||
description: string | null
|
||||
color: string | null
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface RootlyListServicesResponse extends ToolResponse {
|
||||
output: {
|
||||
services: RootlyServiceData[]
|
||||
totalCount: number
|
||||
}
|
||||
}
|
||||
|
||||
/** List Severities */
|
||||
export interface RootlyListSeveritiesParams extends RootlyBaseParams {
|
||||
search?: string
|
||||
pageSize?: number
|
||||
pageNumber?: number
|
||||
}
|
||||
|
||||
interface RootlySeverityData {
|
||||
id: string | null
|
||||
name: string
|
||||
slug: string | null
|
||||
description: string | null
|
||||
severity: string | null
|
||||
color: string | null
|
||||
position: number | null
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface RootlyListSeveritiesResponse extends ToolResponse {
|
||||
output: {
|
||||
severities: RootlySeverityData[]
|
||||
totalCount: number
|
||||
}
|
||||
}
|
||||
|
||||
/** List Retrospectives */
|
||||
export interface RootlyListRetrospectivesParams extends RootlyBaseParams {
|
||||
status?: string
|
||||
search?: string
|
||||
pageSize?: number
|
||||
pageNumber?: number
|
||||
}
|
||||
|
||||
interface RootlyRetrospectiveData {
|
||||
id: string | null
|
||||
title: string
|
||||
status: string | null
|
||||
url: string | null
|
||||
startedAt: string | null
|
||||
mitigatedAt: string | null
|
||||
resolvedAt: string | null
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface RootlyListRetrospectivesResponse extends ToolResponse {
|
||||
output: {
|
||||
retrospectives: RootlyRetrospectiveData[]
|
||||
totalCount: number
|
||||
}
|
||||
}
|
||||
|
||||
/** List Teams */
|
||||
export interface RootlyListTeamsParams extends RootlyBaseParams {
|
||||
search?: string
|
||||
pageSize?: number
|
||||
pageNumber?: number
|
||||
}
|
||||
|
||||
interface RootlyTeamData {
|
||||
id: string | null
|
||||
name: string
|
||||
slug: string | null
|
||||
description: string | null
|
||||
color: string | null
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface RootlyListTeamsResponse extends ToolResponse {
|
||||
output: {
|
||||
teams: RootlyTeamData[]
|
||||
totalCount: number
|
||||
}
|
||||
}
|
||||
|
||||
/** List Environments */
|
||||
export interface RootlyListEnvironmentsParams extends RootlyBaseParams {
|
||||
search?: string
|
||||
pageSize?: number
|
||||
pageNumber?: number
|
||||
}
|
||||
|
||||
interface RootlyEnvironmentData {
|
||||
id: string | null
|
||||
name: string
|
||||
slug: string | null
|
||||
description: string | null
|
||||
color: string | null
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface RootlyListEnvironmentsResponse extends ToolResponse {
|
||||
output: {
|
||||
environments: RootlyEnvironmentData[]
|
||||
totalCount: number
|
||||
}
|
||||
}
|
||||
|
||||
/** List Incident Types */
|
||||
export interface RootlyListIncidentTypesParams extends RootlyBaseParams {
|
||||
search?: string
|
||||
pageSize?: number
|
||||
pageNumber?: number
|
||||
}
|
||||
|
||||
interface RootlyIncidentTypeData {
|
||||
id: string | null
|
||||
name: string
|
||||
slug: string | null
|
||||
description: string | null
|
||||
color: string | null
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface RootlyListIncidentTypesResponse extends ToolResponse {
|
||||
output: {
|
||||
incidentTypes: RootlyIncidentTypeData[]
|
||||
totalCount: number
|
||||
}
|
||||
}
|
||||
|
||||
/** List Causes */
|
||||
export interface RootlyListCausesParams extends RootlyBaseParams {
|
||||
search?: string
|
||||
pageSize?: number
|
||||
pageNumber?: number
|
||||
}
|
||||
|
||||
interface RootlyCauseData {
|
||||
id: string | null
|
||||
name: string
|
||||
slug: string | null
|
||||
description: string | null
|
||||
position: number | null
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface RootlyListCausesResponse extends ToolResponse {
|
||||
output: {
|
||||
causes: RootlyCauseData[]
|
||||
totalCount: number
|
||||
}
|
||||
}
|
||||
|
||||
/** List Playbooks */
|
||||
export interface RootlyListPlaybooksParams extends RootlyBaseParams {
|
||||
pageSize?: number
|
||||
pageNumber?: number
|
||||
}
|
||||
|
||||
interface RootlyPlaybookData {
|
||||
id: string | null
|
||||
title: string
|
||||
summary: string | null
|
||||
externalUrl: string | null
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface RootlyListPlaybooksResponse extends ToolResponse {
|
||||
output: {
|
||||
playbooks: RootlyPlaybookData[]
|
||||
totalCount: number
|
||||
}
|
||||
}
|
||||
|
||||
/** Delete Incident */
|
||||
export interface RootlyDeleteIncidentParams extends RootlyBaseParams {
|
||||
incidentId: string
|
||||
}
|
||||
|
||||
export interface RootlyDeleteIncidentResponse extends ToolResponse {
|
||||
output: {
|
||||
success: boolean
|
||||
message: string
|
||||
}
|
||||
}
|
||||
|
||||
/** Action Item Data */
|
||||
interface RootlyActionItemData {
|
||||
id: string | null
|
||||
summary: string
|
||||
description: string | null
|
||||
kind: string | null
|
||||
priority: string | null
|
||||
status: string | null
|
||||
dueDate: string | null
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
/** Create Action Item */
|
||||
export interface RootlyCreateActionItemParams extends RootlyBaseParams {
|
||||
incidentId: string
|
||||
summary: string
|
||||
description?: string
|
||||
kind?: string
|
||||
priority?: string
|
||||
status?: string
|
||||
assignedToUserId?: string
|
||||
dueDate?: string
|
||||
}
|
||||
|
||||
export interface RootlyCreateActionItemResponse extends ToolResponse {
|
||||
output: {
|
||||
actionItem: RootlyActionItemData
|
||||
}
|
||||
}
|
||||
|
||||
/** List Action Items */
|
||||
export interface RootlyListActionItemsParams extends RootlyBaseParams {
|
||||
incidentId: string
|
||||
pageSize?: number
|
||||
pageNumber?: number
|
||||
}
|
||||
|
||||
export interface RootlyListActionItemsResponse extends ToolResponse {
|
||||
output: {
|
||||
actionItems: RootlyActionItemData[]
|
||||
totalCount: number
|
||||
}
|
||||
}
|
||||
|
||||
/** List Functionalities */
|
||||
export interface RootlyListFunctionalitiesParams extends RootlyBaseParams {
|
||||
search?: string
|
||||
pageSize?: number
|
||||
pageNumber?: number
|
||||
}
|
||||
|
||||
interface RootlyFunctionalityData {
|
||||
id: string | null
|
||||
name: string
|
||||
slug: string | null
|
||||
description: string | null
|
||||
color: string | null
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface RootlyListFunctionalitiesResponse extends ToolResponse {
|
||||
output: {
|
||||
functionalities: RootlyFunctionalityData[]
|
||||
totalCount: number
|
||||
}
|
||||
}
|
||||
|
||||
/** Get Alert */
|
||||
export interface RootlyGetAlertParams extends RootlyBaseParams {
|
||||
alertId: string
|
||||
}
|
||||
|
||||
export interface RootlyGetAlertResponse extends ToolResponse {
|
||||
output: {
|
||||
alert: RootlyAlertData
|
||||
}
|
||||
}
|
||||
|
||||
/** Update Alert */
|
||||
export interface RootlyUpdateAlertParams extends RootlyBaseParams {
|
||||
alertId: string
|
||||
summary?: string
|
||||
description?: string
|
||||
source?: string
|
||||
serviceIds?: string
|
||||
groupIds?: string
|
||||
environmentIds?: string
|
||||
externalId?: string
|
||||
externalUrl?: string
|
||||
deduplicationKey?: string
|
||||
}
|
||||
|
||||
export interface RootlyUpdateAlertResponse extends ToolResponse {
|
||||
output: {
|
||||
alert: RootlyAlertData
|
||||
}
|
||||
}
|
||||
|
||||
/** Acknowledge Alert */
|
||||
export interface RootlyAcknowledgeAlertParams extends RootlyBaseParams {
|
||||
alertId: string
|
||||
}
|
||||
|
||||
export interface RootlyAcknowledgeAlertResponse extends ToolResponse {
|
||||
output: {
|
||||
alert: RootlyAlertData
|
||||
}
|
||||
}
|
||||
|
||||
/** Resolve Alert */
|
||||
export interface RootlyResolveAlertParams extends RootlyBaseParams {
|
||||
alertId: string
|
||||
resolutionMessage?: string
|
||||
resolveRelatedIncidents?: boolean
|
||||
}
|
||||
|
||||
export interface RootlyResolveAlertResponse extends ToolResponse {
|
||||
output: {
|
||||
alert: RootlyAlertData
|
||||
}
|
||||
}
|
||||
|
||||
/** List Users */
|
||||
export interface RootlyListUsersParams extends RootlyBaseParams {
|
||||
search?: string
|
||||
email?: string
|
||||
pageSize?: number
|
||||
pageNumber?: number
|
||||
}
|
||||
|
||||
interface RootlyUserData {
|
||||
id: string | null
|
||||
email: string
|
||||
firstName: string | null
|
||||
lastName: string | null
|
||||
fullName: string | null
|
||||
timeZone: string | null
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface RootlyListUsersResponse extends ToolResponse {
|
||||
output: {
|
||||
users: RootlyUserData[]
|
||||
totalCount: number
|
||||
}
|
||||
}
|
||||
|
||||
/** List On-Calls */
|
||||
export interface RootlyListOnCallsParams extends RootlyBaseParams {
|
||||
scheduleIds?: string
|
||||
escalationPolicyIds?: string
|
||||
userIds?: string
|
||||
serviceIds?: string
|
||||
}
|
||||
|
||||
interface RootlyOnCallData {
|
||||
id: string | null
|
||||
userId: string | null
|
||||
userName: string | null
|
||||
scheduleId: string | null
|
||||
scheduleName: string | null
|
||||
escalationPolicyId: string | null
|
||||
startTime: string | null
|
||||
endTime: string | null
|
||||
}
|
||||
|
||||
export interface RootlyListOnCallsResponse extends ToolResponse {
|
||||
output: {
|
||||
onCalls: RootlyOnCallData[]
|
||||
totalCount: number
|
||||
}
|
||||
}
|
||||
|
||||
/** List Schedules */
|
||||
export interface RootlyListSchedulesParams extends RootlyBaseParams {
|
||||
search?: string
|
||||
pageSize?: number
|
||||
pageNumber?: number
|
||||
}
|
||||
|
||||
interface RootlyScheduleData {
|
||||
id: string | null
|
||||
name: string
|
||||
description: string | null
|
||||
allTimeCoverage: boolean | null
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface RootlyListSchedulesResponse extends ToolResponse {
|
||||
output: {
|
||||
schedules: RootlyScheduleData[]
|
||||
totalCount: number
|
||||
}
|
||||
}
|
||||
|
||||
/** List Escalation Policies */
|
||||
export interface RootlyListEscalationPoliciesParams extends RootlyBaseParams {
|
||||
search?: string
|
||||
pageSize?: number
|
||||
pageNumber?: number
|
||||
}
|
||||
|
||||
interface RootlyEscalationPolicyData {
|
||||
id: string | null
|
||||
name: string
|
||||
description: string | null
|
||||
repeatCount: number | null
|
||||
groupIds: string[] | null
|
||||
serviceIds: string[] | null
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface RootlyListEscalationPoliciesResponse extends ToolResponse {
|
||||
output: {
|
||||
escalationPolicies: RootlyEscalationPolicyData[]
|
||||
totalCount: number
|
||||
}
|
||||
}
|
||||
|
||||
/** Shared incident-action response (returns the standard incident object) */
|
||||
export interface RootlyIncidentActionResponse extends ToolResponse {
|
||||
output: {
|
||||
incident: RootlyIncidentData
|
||||
}
|
||||
}
|
||||
|
||||
/** Shared alert-action response (returns the standard alert object) */
|
||||
export interface RootlyAlertActionResponse extends ToolResponse {
|
||||
output: {
|
||||
alert: RootlyAlertData
|
||||
}
|
||||
}
|
||||
|
||||
/** Mitigate Incident */
|
||||
export interface RootlyMitigateIncidentParams extends RootlyBaseParams {
|
||||
incidentId: string
|
||||
mitigationMessage?: string
|
||||
}
|
||||
|
||||
/** Resolve Incident */
|
||||
export interface RootlyResolveIncidentParams extends RootlyBaseParams {
|
||||
incidentId: string
|
||||
resolutionMessage?: string
|
||||
}
|
||||
|
||||
/** Assign Incident Role */
|
||||
export interface RootlyAssignIncidentRoleParams extends RootlyBaseParams {
|
||||
incidentId: string
|
||||
userId: string
|
||||
incidentRoleId: string
|
||||
}
|
||||
|
||||
/** Unassign Incident Role */
|
||||
export interface RootlyUnassignIncidentRoleParams extends RootlyBaseParams {
|
||||
incidentId: string
|
||||
userId: string
|
||||
incidentRoleId: string
|
||||
}
|
||||
|
||||
/** Add Subscribers */
|
||||
export interface RootlyAddSubscribersParams extends RootlyBaseParams {
|
||||
incidentId: string
|
||||
userIds: string
|
||||
}
|
||||
|
||||
/** Remove Subscribers */
|
||||
export interface RootlyRemoveSubscribersParams extends RootlyBaseParams {
|
||||
incidentId: string
|
||||
userIds: string
|
||||
}
|
||||
|
||||
/** Create Status Page Event */
|
||||
export interface RootlyCreateStatusPageEventParams extends RootlyBaseParams {
|
||||
incidentId: string
|
||||
event: string
|
||||
statusPageId?: string
|
||||
status?: string
|
||||
notifySubscribers?: boolean
|
||||
shouldTweet?: boolean
|
||||
}
|
||||
|
||||
interface RootlyStatusPageEventData {
|
||||
id: string | null
|
||||
event: string
|
||||
statusPageId: string | null
|
||||
status: string | null
|
||||
notifySubscribers: boolean | null
|
||||
shouldTweet: boolean | null
|
||||
startedAt: string | null
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface RootlyCreateStatusPageEventResponse extends ToolResponse {
|
||||
output: {
|
||||
statusPageEvent: RootlyStatusPageEventData
|
||||
}
|
||||
}
|
||||
|
||||
/** Update Action Item */
|
||||
export interface RootlyUpdateActionItemParams extends RootlyBaseParams {
|
||||
actionItemId: string
|
||||
summary?: string
|
||||
description?: string
|
||||
kind?: string
|
||||
priority?: string
|
||||
status?: string
|
||||
assignedToUserId?: string
|
||||
dueDate?: string
|
||||
}
|
||||
|
||||
export interface RootlyUpdateActionItemResponse extends ToolResponse {
|
||||
output: {
|
||||
actionItem: RootlyActionItemData
|
||||
}
|
||||
}
|
||||
|
||||
/** Delete Action Item */
|
||||
export interface RootlyDeleteActionItemParams extends RootlyBaseParams {
|
||||
actionItemId: string
|
||||
}
|
||||
|
||||
export interface RootlyDeleteActionItemResponse extends ToolResponse {
|
||||
output: {
|
||||
success: boolean
|
||||
message: string
|
||||
}
|
||||
}
|
||||
|
||||
/** Snooze Alert */
|
||||
export interface RootlySnoozeAlertParams extends RootlyBaseParams {
|
||||
alertId: string
|
||||
delayMinutes: number
|
||||
}
|
||||
|
||||
/** Escalate Alert */
|
||||
export interface RootlyEscalateAlertParams extends RootlyBaseParams {
|
||||
alertId: string
|
||||
escalationPolicyId?: string
|
||||
escalationPolicyLevel?: number
|
||||
}
|
||||
|
||||
/** List Incident Events */
|
||||
export interface RootlyListIncidentEventsParams extends RootlyBaseParams {
|
||||
incidentId: string
|
||||
pageSize?: number
|
||||
pageNumber?: number
|
||||
}
|
||||
|
||||
interface RootlyIncidentEventData {
|
||||
id: string | null
|
||||
event: string
|
||||
visibility: string | null
|
||||
occurredAt: string | null
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface RootlyListIncidentEventsResponse extends ToolResponse {
|
||||
output: {
|
||||
events: RootlyIncidentEventData[]
|
||||
totalCount: number
|
||||
}
|
||||
}
|
||||
|
||||
/** Run Workflow */
|
||||
export interface RootlyRunWorkflowParams extends RootlyBaseParams {
|
||||
workflowId: string
|
||||
incidentId?: string
|
||||
alertId?: string
|
||||
immediate?: boolean
|
||||
checkConditions?: boolean
|
||||
}
|
||||
|
||||
interface RootlyWorkflowRunData {
|
||||
id: string | null
|
||||
workflowId: string | null
|
||||
status: string | null
|
||||
statusMessage: string | null
|
||||
triggeredBy: string | null
|
||||
incidentId: string | null
|
||||
alertId: string | null
|
||||
startedAt: string | null
|
||||
completedAt: string | null
|
||||
failedAt: string | null
|
||||
canceledAt: string | null
|
||||
}
|
||||
|
||||
export interface RootlyRunWorkflowResponse extends ToolResponse {
|
||||
output: {
|
||||
workflowRun: RootlyWorkflowRunData
|
||||
}
|
||||
}
|
||||
|
||||
/** List Incident Roles */
|
||||
export interface RootlyListIncidentRolesParams extends RootlyBaseParams {
|
||||
search?: string
|
||||
pageSize?: number
|
||||
pageNumber?: number
|
||||
}
|
||||
|
||||
interface RootlyIncidentRoleData {
|
||||
id: string | null
|
||||
name: string
|
||||
slug: string | null
|
||||
summary: string | null
|
||||
description: string | null
|
||||
position: number | null
|
||||
optional: boolean | null
|
||||
enabled: boolean | null
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface RootlyListIncidentRolesResponse extends ToolResponse {
|
||||
output: {
|
||||
incidentRoles: RootlyIncidentRoleData[]
|
||||
totalCount: number
|
||||
}
|
||||
}
|
||||
|
||||
/** Union of all responses */
|
||||
export type RootlyResponse =
|
||||
| RootlyCreateIncidentResponse
|
||||
| RootlyGetIncidentResponse
|
||||
| RootlyUpdateIncidentResponse
|
||||
| RootlyDeleteIncidentResponse
|
||||
| RootlyListIncidentsResponse
|
||||
| RootlyCreateAlertResponse
|
||||
| RootlyGetAlertResponse
|
||||
| RootlyUpdateAlertResponse
|
||||
| RootlyAcknowledgeAlertResponse
|
||||
| RootlyResolveAlertResponse
|
||||
| RootlyListAlertsResponse
|
||||
| RootlyAddIncidentEventResponse
|
||||
| RootlyCreateActionItemResponse
|
||||
| RootlyListActionItemsResponse
|
||||
| RootlyListServicesResponse
|
||||
| RootlyListSeveritiesResponse
|
||||
| RootlyListRetrospectivesResponse
|
||||
| RootlyListTeamsResponse
|
||||
| RootlyListEnvironmentsResponse
|
||||
| RootlyListIncidentTypesResponse
|
||||
| RootlyListFunctionalitiesResponse
|
||||
| RootlyListCausesResponse
|
||||
| RootlyListPlaybooksResponse
|
||||
| RootlyListUsersResponse
|
||||
| RootlyListOnCallsResponse
|
||||
| RootlyListSchedulesResponse
|
||||
| RootlyListEscalationPoliciesResponse
|
||||
| RootlyIncidentActionResponse
|
||||
| RootlyAlertActionResponse
|
||||
| RootlyCreateStatusPageEventResponse
|
||||
| RootlyUpdateActionItemResponse
|
||||
| RootlyDeleteActionItemResponse
|
||||
| RootlyListIncidentEventsResponse
|
||||
| RootlyRunWorkflowResponse
|
||||
| RootlyListIncidentRolesResponse
|
||||
@@ -0,0 +1,127 @@
|
||||
import type {
|
||||
RootlyIncidentActionResponse,
|
||||
RootlyUnassignIncidentRoleParams,
|
||||
} from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyUnassignIncidentRoleTool: ToolConfig<
|
||||
RootlyUnassignIncidentRoleParams,
|
||||
RootlyIncidentActionResponse
|
||||
> = {
|
||||
id: 'rootly_unassign_incident_role',
|
||||
name: 'Rootly Unassign Incident Role',
|
||||
description: 'Remove an incident role assignment from a user on a Rootly incident.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
incidentId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the incident',
|
||||
},
|
||||
userId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the user to unassign (use List Users to find IDs)',
|
||||
},
|
||||
incidentRoleId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the incident role to remove (use List Incident Roles to find IDs)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.rootly.com/v1/incidents/${params.incidentId.trim()}/unassign_role_from_user`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
body: (params) => ({
|
||||
data: {
|
||||
type: 'incidents',
|
||||
attributes: {
|
||||
user_id: params.userId.trim(),
|
||||
incident_role_id: params.incidentRoleId.trim(),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { incident: {} as RootlyIncidentActionResponse['output']['incident'] },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const attrs = data.data?.attributes || {}
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
incident: {
|
||||
id: data.data?.id ?? null,
|
||||
sequentialId: attrs.sequential_id ?? null,
|
||||
title: attrs.title ?? '',
|
||||
slug: attrs.slug ?? null,
|
||||
kind: attrs.kind ?? null,
|
||||
summary: attrs.summary ?? null,
|
||||
status: attrs.status ?? null,
|
||||
private: attrs.private ?? false,
|
||||
url: attrs.url ?? null,
|
||||
shortUrl: attrs.short_url ?? null,
|
||||
severityName: attrs.severity?.data?.attributes?.name ?? null,
|
||||
severityId: attrs.severity?.data?.id ?? null,
|
||||
createdAt: attrs.created_at ?? '',
|
||||
updatedAt: attrs.updated_at ?? '',
|
||||
startedAt: attrs.started_at ?? null,
|
||||
mitigatedAt: attrs.mitigated_at ?? null,
|
||||
resolvedAt: attrs.resolved_at ?? null,
|
||||
closedAt: attrs.closed_at ?? null,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
incident: {
|
||||
type: 'object',
|
||||
description: 'The incident after the role was unassigned',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique incident ID' },
|
||||
sequentialId: { type: 'number', description: 'Sequential incident number' },
|
||||
title: { type: 'string', description: 'Incident title' },
|
||||
slug: { type: 'string', description: 'Incident slug' },
|
||||
kind: { type: 'string', description: 'Incident kind' },
|
||||
summary: { type: 'string', description: 'Incident summary' },
|
||||
status: { type: 'string', description: 'Incident status' },
|
||||
private: { type: 'boolean', description: 'Whether the incident is private' },
|
||||
url: { type: 'string', description: 'URL to the incident' },
|
||||
shortUrl: { type: 'string', description: 'Short URL to the incident' },
|
||||
severityName: { type: 'string', description: 'Severity name' },
|
||||
severityId: { type: 'string', description: 'Severity ID' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
startedAt: { type: 'string', description: 'Start date' },
|
||||
mitigatedAt: { type: 'string', description: 'Mitigation date' },
|
||||
resolvedAt: { type: 'string', description: 'Resolution date' },
|
||||
closedAt: { type: 'string', description: 'Closed date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
import type {
|
||||
RootlyUpdateActionItemParams,
|
||||
RootlyUpdateActionItemResponse,
|
||||
} from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyUpdateActionItemTool: ToolConfig<
|
||||
RootlyUpdateActionItemParams,
|
||||
RootlyUpdateActionItemResponse
|
||||
> = {
|
||||
id: 'rootly_update_action_item',
|
||||
name: 'Rootly Update Action Item',
|
||||
description: 'Update a Rootly incident action item (status, priority, assignee, etc.).',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
actionItemId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the action item to update',
|
||||
},
|
||||
summary: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Updated action item title',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Updated description',
|
||||
},
|
||||
kind: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The kind of action item (task, follow_up)',
|
||||
},
|
||||
priority: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Priority level (high, medium, low)',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Action item status (open, in_progress, cancelled, done)',
|
||||
},
|
||||
assignedToUserId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The user ID to assign the action item to',
|
||||
},
|
||||
dueDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Due date for the action item',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.rootly.com/v1/action_items/${params.actionItemId.trim()}`,
|
||||
method: 'PUT',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
body: (params) => {
|
||||
const attributes: Record<string, unknown> = {}
|
||||
if (params.summary) attributes.summary = params.summary
|
||||
if (params.description) attributes.description = params.description
|
||||
if (params.kind) attributes.kind = params.kind
|
||||
if (params.priority) attributes.priority = params.priority
|
||||
if (params.status) attributes.status = params.status
|
||||
if (params.assignedToUserId) {
|
||||
const numericId = Number.parseInt(params.assignedToUserId, 10)
|
||||
if (!Number.isNaN(numericId)) attributes.assigned_to_user_id = numericId
|
||||
}
|
||||
if (params.dueDate) attributes.due_date = params.dueDate
|
||||
return {
|
||||
data: { type: 'incident_action_items', id: params.actionItemId.trim(), attributes },
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { actionItem: {} as RootlyUpdateActionItemResponse['output']['actionItem'] },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const attrs = data.data?.attributes || {}
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
actionItem: {
|
||||
id: data.data?.id ?? null,
|
||||
summary: attrs.summary ?? '',
|
||||
description: attrs.description ?? null,
|
||||
kind: attrs.kind ?? null,
|
||||
priority: attrs.priority ?? null,
|
||||
status: attrs.status ?? null,
|
||||
dueDate: attrs.due_date ?? null,
|
||||
createdAt: attrs.created_at ?? '',
|
||||
updatedAt: attrs.updated_at ?? '',
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
actionItem: {
|
||||
type: 'object',
|
||||
description: 'The updated action item',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique action item ID' },
|
||||
summary: { type: 'string', description: 'Action item title' },
|
||||
description: { type: 'string', description: 'Action item description' },
|
||||
kind: { type: 'string', description: 'Action item kind (task, follow_up)' },
|
||||
priority: { type: 'string', description: 'Priority level' },
|
||||
status: { type: 'string', description: 'Action item status' },
|
||||
dueDate: { type: 'string', description: 'Due date' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
import type { RootlyUpdateAlertParams, RootlyUpdateAlertResponse } from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyUpdateAlertTool: ToolConfig<RootlyUpdateAlertParams, RootlyUpdateAlertResponse> =
|
||||
{
|
||||
id: 'rootly_update_alert',
|
||||
name: 'Rootly Update Alert',
|
||||
description: 'Update an existing alert in Rootly.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
alertId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the alert to update',
|
||||
},
|
||||
summary: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Updated alert summary',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Updated alert description',
|
||||
},
|
||||
source: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Updated alert source',
|
||||
},
|
||||
serviceIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated service IDs to attach',
|
||||
},
|
||||
groupIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated team/group IDs to attach',
|
||||
},
|
||||
environmentIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated environment IDs to attach',
|
||||
},
|
||||
externalId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Updated external ID',
|
||||
},
|
||||
externalUrl: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Updated external URL',
|
||||
},
|
||||
deduplicationKey: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Updated deduplication key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.rootly.com/v1/alerts/${params.alertId.trim()}`,
|
||||
method: 'PATCH',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
body: (params) => {
|
||||
const attributes: Record<string, unknown> = {}
|
||||
if (params.summary) attributes.summary = params.summary
|
||||
if (params.description) attributes.description = params.description
|
||||
if (params.source) attributes.source = params.source
|
||||
if (params.externalId) attributes.external_id = params.externalId
|
||||
if (params.externalUrl) attributes.external_url = params.externalUrl
|
||||
if (params.deduplicationKey) attributes.deduplication_key = params.deduplicationKey
|
||||
if (params.serviceIds) {
|
||||
attributes.service_ids = params.serviceIds.split(',').map((s: string) => s.trim())
|
||||
}
|
||||
if (params.groupIds) {
|
||||
attributes.group_ids = params.groupIds.split(',').map((s: string) => s.trim())
|
||||
}
|
||||
if (params.environmentIds) {
|
||||
attributes.environment_ids = params.environmentIds.split(',').map((s: string) => s.trim())
|
||||
}
|
||||
return { data: { type: 'alerts', id: params.alertId.trim(), attributes } }
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { alert: {} as RootlyUpdateAlertResponse['output']['alert'] },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const attrs = data.data?.attributes || {}
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
alert: {
|
||||
id: data.data?.id ?? null,
|
||||
shortId: attrs.short_id ?? null,
|
||||
summary: attrs.summary ?? '',
|
||||
description: attrs.description ?? null,
|
||||
source: attrs.source ?? null,
|
||||
status: attrs.status ?? null,
|
||||
externalId: attrs.external_id ?? null,
|
||||
externalUrl: attrs.external_url ?? null,
|
||||
deduplicationKey: attrs.deduplication_key ?? null,
|
||||
createdAt: attrs.created_at ?? '',
|
||||
updatedAt: attrs.updated_at ?? '',
|
||||
startedAt: attrs.started_at ?? null,
|
||||
endedAt: attrs.ended_at ?? null,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
alert: {
|
||||
type: 'object',
|
||||
description: 'The updated alert',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique alert ID' },
|
||||
shortId: { type: 'string', description: 'Short alert ID' },
|
||||
summary: { type: 'string', description: 'Alert summary' },
|
||||
description: { type: 'string', description: 'Alert description' },
|
||||
source: { type: 'string', description: 'Alert source' },
|
||||
status: { type: 'string', description: 'Alert status' },
|
||||
externalId: { type: 'string', description: 'External ID' },
|
||||
externalUrl: { type: 'string', description: 'External URL' },
|
||||
deduplicationKey: { type: 'string', description: 'Deduplication key' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
startedAt: { type: 'string', description: 'Start date' },
|
||||
endedAt: { type: 'string', description: 'End date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
import type { RootlyUpdateIncidentParams, RootlyUpdateIncidentResponse } from '@/tools/rootly/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rootlyUpdateIncidentTool: ToolConfig<
|
||||
RootlyUpdateIncidentParams,
|
||||
RootlyUpdateIncidentResponse
|
||||
> = {
|
||||
id: 'rootly_update_incident',
|
||||
name: 'Rootly Update Incident',
|
||||
description: 'Update an existing incident in Rootly (status, severity, summary, etc.).',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rootly API key',
|
||||
},
|
||||
incidentId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the incident to update',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Updated incident title',
|
||||
},
|
||||
summary: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Updated incident summary',
|
||||
},
|
||||
severityId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Updated severity ID',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Updated status (in_triage, started, detected, acknowledged, mitigated, resolved, closed, cancelled, scheduled, in_progress, completed)',
|
||||
},
|
||||
kind: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Incident kind (normal, normal_sub, test, test_sub, example, example_sub, backfilled, scheduled, scheduled_sub)',
|
||||
},
|
||||
private: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Set incident as private (cannot be undone)',
|
||||
},
|
||||
serviceIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated service IDs',
|
||||
},
|
||||
environmentIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated environment IDs',
|
||||
},
|
||||
groupIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated team/group IDs',
|
||||
},
|
||||
incidentTypeIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated incident type IDs to attach',
|
||||
},
|
||||
functionalityIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated functionality IDs to attach',
|
||||
},
|
||||
labels: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Labels as JSON object, e.g. {"platform":"osx","version":"1.29"}',
|
||||
},
|
||||
mitigationMessage: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'How was the incident mitigated?',
|
||||
},
|
||||
resolutionMessage: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'How was the incident resolved?',
|
||||
},
|
||||
cancellationMessage: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Why was the incident cancelled?',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.rootly.com/v1/incidents/${params.incidentId.trim()}`,
|
||||
method: 'PUT',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
body: (params) => {
|
||||
const attributes: Record<string, unknown> = {}
|
||||
if (params.title) attributes.title = params.title
|
||||
if (params.summary) attributes.summary = params.summary
|
||||
if (params.severityId) attributes.severity_id = params.severityId
|
||||
if (params.status) attributes.status = params.status
|
||||
if (params.kind) attributes.kind = params.kind
|
||||
if (params.private !== undefined) attributes.private = params.private
|
||||
if (params.mitigationMessage) attributes.mitigation_message = params.mitigationMessage
|
||||
if (params.resolutionMessage) attributes.resolution_message = params.resolutionMessage
|
||||
if (params.cancellationMessage) attributes.cancellation_message = params.cancellationMessage
|
||||
if (params.incidentTypeIds) {
|
||||
attributes.incident_type_ids = params.incidentTypeIds
|
||||
.split(',')
|
||||
.map((s: string) => s.trim())
|
||||
}
|
||||
if (params.functionalityIds) {
|
||||
attributes.functionality_ids = params.functionalityIds
|
||||
.split(',')
|
||||
.map((s: string) => s.trim())
|
||||
}
|
||||
if (params.labels) {
|
||||
try {
|
||||
attributes.labels = JSON.parse(params.labels)
|
||||
} catch {
|
||||
attributes.labels = params.labels
|
||||
}
|
||||
}
|
||||
if (params.serviceIds) {
|
||||
attributes.service_ids = params.serviceIds.split(',').map((s: string) => s.trim())
|
||||
}
|
||||
if (params.environmentIds) {
|
||||
attributes.environment_ids = params.environmentIds.split(',').map((s: string) => s.trim())
|
||||
}
|
||||
if (params.groupIds) {
|
||||
attributes.group_ids = params.groupIds.split(',').map((s: string) => s.trim())
|
||||
}
|
||||
return { data: { type: 'incidents', id: params.incidentId.trim(), attributes } }
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: { incident: {} as RootlyUpdateIncidentResponse['output']['incident'] },
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const attrs = data.data?.attributes || {}
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
incident: {
|
||||
id: data.data?.id ?? null,
|
||||
sequentialId: attrs.sequential_id ?? null,
|
||||
title: attrs.title ?? '',
|
||||
slug: attrs.slug ?? null,
|
||||
kind: attrs.kind ?? null,
|
||||
summary: attrs.summary ?? null,
|
||||
status: attrs.status ?? null,
|
||||
private: attrs.private ?? false,
|
||||
url: attrs.url ?? null,
|
||||
shortUrl: attrs.short_url ?? null,
|
||||
severityName: attrs.severity?.data?.attributes?.name ?? null,
|
||||
severityId: attrs.severity?.data?.id ?? null,
|
||||
createdAt: attrs.created_at ?? '',
|
||||
updatedAt: attrs.updated_at ?? '',
|
||||
startedAt: attrs.started_at ?? null,
|
||||
mitigatedAt: attrs.mitigated_at ?? null,
|
||||
resolvedAt: attrs.resolved_at ?? null,
|
||||
closedAt: attrs.closed_at ?? null,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
incident: {
|
||||
type: 'object',
|
||||
description: 'The updated incident',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique incident ID' },
|
||||
sequentialId: { type: 'number', description: 'Sequential incident number' },
|
||||
title: { type: 'string', description: 'Incident title' },
|
||||
slug: { type: 'string', description: 'Incident slug' },
|
||||
kind: { type: 'string', description: 'Incident kind' },
|
||||
summary: { type: 'string', description: 'Incident summary' },
|
||||
status: { type: 'string', description: 'Incident status' },
|
||||
private: { type: 'boolean', description: 'Whether the incident is private' },
|
||||
url: { type: 'string', description: 'URL to the incident' },
|
||||
shortUrl: { type: 'string', description: 'Short URL to the incident' },
|
||||
severityName: { type: 'string', description: 'Severity name' },
|
||||
severityId: { type: 'string', description: 'Severity ID' },
|
||||
createdAt: { type: 'string', description: 'Creation date' },
|
||||
updatedAt: { type: 'string', description: 'Last update date' },
|
||||
startedAt: { type: 'string', description: 'Start date' },
|
||||
mitigatedAt: { type: 'string', description: 'Mitigation date' },
|
||||
resolvedAt: { type: 'string', description: 'Resolution date' },
|
||||
closedAt: { type: 'string', description: 'Closed date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user