feat: telemetry alert rules with webhook delivery

core.alert_rules (migration 00012) — heartbeat_silence and error_spike
rules evaluated by a 60s backend ticker. Transition-based firing:
alert.fired on breach, alert.resolved on recovery, both first-class
webhook event types riding the signed dispatcher + realtime stream.
Dashboard manages rules on the Integrations page.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomas DvorakandDevin committed 2026-09-24 19:09:19 +02:00
1 parent 345cf7918b
commit 2f4de543a9
28 files changed
+1693 -10

No files matched your search

@@ -8,6 +8,9 @@ export { OpenAPI } from './core/OpenAPI';
export type { OpenAPIConfig } from './core/OpenAPI';
export type { AcceptInvitationRequest } from './models/AcceptInvitationRequest';
export { AlertRule } from './models/AlertRule';
export type { AlertRuleConfig } from './models/AlertRuleConfig';
export type { AlertRuleListResponse } from './models/AlertRuleListResponse';
export type { ApiKey } from './models/ApiKey';
export type { ApiKeyID } from './models/ApiKeyID';
export type { AuditAction } from './models/AuditAction';
@@ -130,6 +133,7 @@ export { UpdateProjectMemberRoleRequest } from './models/UpdateProjectMemberRole
export type { UpdateProjectRequest } from './models/UpdateProjectRequest';
export type { UpdateScheduledJobRequest } from './models/UpdateScheduledJobRequest';
export type { UpdateWebhookRequest } from './models/UpdateWebhookRequest';
export { UpsertAlertRuleRequest } from './models/UpsertAlertRuleRequest';
export type { UserID } from './models/UserID';
export type { UserSummary } from './models/UserSummary';
export type { Webhook } from './models/Webhook';
@@ -140,6 +144,9 @@ export type { WebhookID } from './models/WebhookID';
export type { WebhookListResponse } from './models/WebhookListResponse';
export { $AcceptInvitationRequest } from './schemas/$AcceptInvitationRequest';
export { $AlertRule } from './schemas/$AlertRule';
export { $AlertRuleConfig } from './schemas/$AlertRuleConfig';
export { $AlertRuleListResponse } from './schemas/$AlertRuleListResponse';
export { $ApiKey } from './schemas/$ApiKey';
export { $ApiKeyID } from './schemas/$ApiKeyID';
export { $AuditAction } from './schemas/$AuditAction';
@@ -262,6 +269,7 @@ export { $UpdateProjectMemberRoleRequest } from './schemas/$UpdateProjectMemberR
export { $UpdateProjectRequest } from './schemas/$UpdateProjectRequest';
export { $UpdateScheduledJobRequest } from './schemas/$UpdateScheduledJobRequest';
export { $UpdateWebhookRequest } from './schemas/$UpdateWebhookRequest';
export { $UpsertAlertRuleRequest } from './schemas/$UpsertAlertRuleRequest';
export { $UserID } from './schemas/$UserID';
export { $UserSummary } from './schemas/$UserSummary';
export { $Webhook } from './schemas/$Webhook';
@@ -271,6 +279,7 @@ export { $WebhookDeliveryListResponse } from './schemas/$WebhookDeliveryListResp
export { $WebhookID } from './schemas/$WebhookID';
export { $WebhookListResponse } from './schemas/$WebhookListResponse';
export { AlertsService } from './services/AlertsService';
export { AutomationService } from './services/AutomationService';
export { CollectionsService } from './services/CollectionsService';
export { DatabasesService } from './services/DatabasesService';
@@ -0,0 +1,26 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { AlertRuleConfig } from './AlertRuleConfig';
export type AlertRule = {
id: string;
project_id: string;
name: string;
kind: AlertRule.kind;
config: AlertRuleConfig;
enabled: boolean;
/**
* Component names currently in breached state.
*/
firing: Array<string>;
created_at: string;
updated_at: string;
};
export namespace AlertRule {
export enum kind {
HEARTBEAT_SILENCE = 'heartbeat_silence',
ERROR_SPIKE = 'error_spike',
}
}
@@ -0,0 +1,23 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type AlertRuleConfig = {
/**
* Restrict the rule to one component name; empty = all components.
*/
component?: string;
/**
* heartbeat_silence — fire when a component has not heartbeated for this many minutes (1–10080).
*/
threshold_minutes?: number;
/**
* error_spike — fire when a component logs at least this many errors inside the window.
*/
threshold_count?: number;
/**
* error_spike — sliding window size (default 15, 1–1440).
*/
window_minutes?: number;
};
@@ -0,0 +1,9 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { AlertRule } from './AlertRule';
export type AlertRuleListResponse = {
items: Array<AlertRule>;
};
@@ -8,7 +8,7 @@ export type CreateWebhookRequest = {
* HMAC key for X-Primora-Signature. Generated when omitted.
*/
secret?: string;
events?: Array<'issue.created' | 'deploy.marker' | 'webhook.test' | 'job.run' | 'document.created' | 'document.updated' | 'document.deleted' | 'object.created' | 'object.updated' | 'object.deleted'>;
events?: Array<'issue.created' | 'deploy.marker' | 'webhook.test' | 'job.run' | 'alert.fired' | 'alert.resolved' | 'document.created' | 'document.updated' | 'document.deleted' | 'object.created' | 'object.updated' | 'object.deleted'>;
enabled?: boolean;
};
@@ -8,7 +8,7 @@ export type UpdateWebhookRequest = {
* Replace the signing secret. Omit to keep the current one.
*/
secret?: string;
events?: Array<'issue.created' | 'deploy.marker' | 'webhook.test' | 'job.run' | 'document.created' | 'document.updated' | 'document.deleted' | 'object.created' | 'object.updated' | 'object.deleted'>;
events?: Array<'issue.created' | 'deploy.marker' | 'webhook.test' | 'job.run' | 'alert.fired' | 'alert.resolved' | 'document.created' | 'document.updated' | 'document.deleted' | 'object.created' | 'object.updated' | 'object.deleted'>;
enabled?: boolean;
};
@@ -0,0 +1,21 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { AlertRuleConfig } from './AlertRuleConfig';
export type UpsertAlertRuleRequest = {
/**
* Env-var-safe name ([A-Za-z_][A-Za-z0-9_]*).
*/
name: string;
kind: UpsertAlertRuleRequest.kind;
config?: AlertRuleConfig;
enabled?: boolean;
};
export namespace UpsertAlertRuleRequest {
export enum kind {
HEARTBEAT_SILENCE = 'heartbeat_silence',
ERROR_SPIKE = 'error_spike',
}
}
@@ -7,7 +7,7 @@ export type Webhook = {
project_id: string;
url: string;
/**
* Event filter (issue.created, deploy.marker, webhook.test, job.run, document.*, object.*). Empty means all events.
* Event filter (issue.created, deploy.marker, webhook.test, job.run, alert.fired, alert.resolved, document.*, object.*). Empty means all events.
*/
events: Array<string>;
enabled: boolean;
@@ -0,0 +1,51 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export const $AlertRule = {
properties: {
id: {
type: 'string',
isRequired: true,
format: 'uuid',
},
project_id: {
type: 'string',
isRequired: true,
format: 'uuid',
},
name: {
type: 'string',
isRequired: true,
},
kind: {
type: 'Enum',
isRequired: true,
},
config: {
type: 'AlertRuleConfig',
isRequired: true,
},
enabled: {
type: 'boolean',
isRequired: true,
},
firing: {
type: 'array',
contains: {
type: 'string',
},
isRequired: true,
},
created_at: {
type: 'string',
isRequired: true,
format: 'date-time',
},
updated_at: {
type: 'string',
isRequired: true,
format: 'date-time',
},
},
} as const;
@@ -0,0 +1,25 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export const $AlertRuleConfig = {
properties: {
component: {
type: 'string',
description: `Restrict the rule to one component name; empty = all components.`,
},
threshold_minutes: {
type: 'number',
description: `heartbeat_silence — fire when a component has not heartbeated for this many minutes (1–10080).`,
},
threshold_count: {
type: 'number',
description: `error_spike — fire when a component logs at least this many errors inside the window.`,
format: 'int64',
},
window_minutes: {
type: 'number',
description: `error_spike — sliding window size (default 15, 1–1440).`,
},
},
} as const;
@@ -0,0 +1,15 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export const $AlertRuleListResponse = {
properties: {
items: {
type: 'array',
contains: {
type: 'AlertRule',
},
isRequired: true,
},
},
} as const;
@@ -0,0 +1,23 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export const $UpsertAlertRuleRequest = {
properties: {
name: {
type: 'string',
description: `Env-var-safe name ([A-Za-z_][A-Za-z0-9_]*).`,
isRequired: true,
},
kind: {
type: 'Enum',
isRequired: true,
},
config: {
type: 'AlertRuleConfig',
},
enabled: {
type: 'boolean',
},
},
} as const;
@@ -0,0 +1,112 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { AlertRule } from '../models/AlertRule';
import type { AlertRuleListResponse } from '../models/AlertRuleListResponse';
import type { UpsertAlertRuleRequest } from '../models/UpsertAlertRuleRequest';
import type { CancelablePromise } from '../core/CancelablePromise';
import { OpenAPI } from '../core/OpenAPI';
import { request as __request } from '../core/request';
export class AlertsService {
/**
* Lists telemetry alert rules for the project.
* @returns AlertRuleListResponse Alert rules
* @throws ApiError
*/
public static listAlertRules({
projectId,
}: {
projectId: string,
}): CancelablePromise<AlertRuleListResponse> {
return __request(OpenAPI, {
method: 'GET',
url: '/projects/{projectID}/alerts',
path: {
'projectID': projectId,
},
errors: {
401: `Error response`,
},
});
}
/**
* Creates an alert rule. Rules fire `alert.fired`/`alert.resolved` domain events on state transitions — subscribe a webhook to those event types to receive them.
* @returns AlertRule Alert rule created
* @throws ApiError
*/
public static createAlertRule({
projectId,
requestBody,
}: {
projectId: string,
requestBody: UpsertAlertRuleRequest,
}): CancelablePromise<AlertRule> {
return __request(OpenAPI, {
method: 'POST',
url: '/projects/{projectID}/alerts',
path: {
'projectID': projectId,
},
body: requestBody,
mediaType: 'application/json',
errors: {
400: `Error response`,
401: `Error response`,
},
});
}
/**
* @returns AlertRule Alert rule updated
* @throws ApiError
*/
public static updateAlertRule({
projectId,
ruleId,
requestBody,
}: {
projectId: string,
ruleId: string,
requestBody: UpsertAlertRuleRequest,
}): CancelablePromise<AlertRule> {
return __request(OpenAPI, {
method: 'PUT',
url: '/projects/{projectID}/alerts/{ruleID}',
path: {
'projectID': projectId,
'ruleID': ruleId,
},
body: requestBody,
mediaType: 'application/json',
errors: {
400: `Error response`,
401: `Error response`,
404: `Error response`,
},
});
}
/**
* @returns void
* @throws ApiError
*/
public static deleteAlertRule({
projectId,
ruleId,
}: {
projectId: string,
ruleId: string,
}): CancelablePromise<void> {
return __request(OpenAPI, {
method: 'DELETE',
url: '/projects/{projectID}/alerts/{ruleID}',
path: {
'projectID': projectId,
'ruleID': ruleId,
},
errors: {
401: `Error response`,
404: `Error response`,
},
});
}
}