@scanwarp/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/correlator.d.ts +24 -0
- package/dist/correlator.js +173 -0
- package/dist/diagnoser.d.ts +26 -0
- package/dist/diagnoser.js +159 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +19 -0
- package/dist/types.d.ts +115 -0
- package/dist/types.js +2 -0
- package/package.json +44 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Event, ProviderStatus } from './types.js';
|
|
2
|
+
export interface CorrelationResult {
|
|
3
|
+
shouldCorrelate: boolean;
|
|
4
|
+
correlationGroup?: string;
|
|
5
|
+
existingIncidentId?: string;
|
|
6
|
+
reason?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class Correlator {
|
|
9
|
+
/**
|
|
10
|
+
* Analyzes if a new event should be correlated with existing events/incidents
|
|
11
|
+
*/
|
|
12
|
+
correlate(newEvent: Event, recentEvents: Event[], openIncidents: Array<{
|
|
13
|
+
id: string;
|
|
14
|
+
events: string[];
|
|
15
|
+
correlation_group?: string;
|
|
16
|
+
}>, providerStatuses: ProviderStatus[]): Promise<CorrelationResult>;
|
|
17
|
+
private checkProviderOutage;
|
|
18
|
+
private findSameEndpoint;
|
|
19
|
+
private extractEndpoint;
|
|
20
|
+
private isCheckoutEndpoint;
|
|
21
|
+
private correlatePaymentAndCheckout;
|
|
22
|
+
private checkMultipleMonitorFailures;
|
|
23
|
+
private findIncidentWithEvent;
|
|
24
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Correlator = void 0;
|
|
4
|
+
class Correlator {
|
|
5
|
+
/**
|
|
6
|
+
* Analyzes if a new event should be correlated with existing events/incidents
|
|
7
|
+
*/
|
|
8
|
+
async correlate(newEvent, recentEvents, openIncidents, providerStatuses) {
|
|
9
|
+
// Rule 1: Check if this is part of a provider outage
|
|
10
|
+
const providerOutage = this.checkProviderOutage(newEvent, providerStatuses);
|
|
11
|
+
if (providerOutage) {
|
|
12
|
+
return {
|
|
13
|
+
shouldCorrelate: true,
|
|
14
|
+
correlationGroup: `provider-${providerOutage.provider}`,
|
|
15
|
+
reason: `${providerOutage.provider} is experiencing ${providerOutage.status}`,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
// Rule 2: Same URL/endpoint within 5 minutes
|
|
19
|
+
const sameEndpoint = this.findSameEndpoint(newEvent, recentEvents);
|
|
20
|
+
if (sameEndpoint) {
|
|
21
|
+
const incident = this.findIncidentWithEvent(sameEndpoint.id, openIncidents);
|
|
22
|
+
if (incident) {
|
|
23
|
+
return {
|
|
24
|
+
shouldCorrelate: true,
|
|
25
|
+
correlationGroup: incident.correlation_group || `endpoint-${this.extractEndpoint(newEvent)}`,
|
|
26
|
+
existingIncidentId: incident.id,
|
|
27
|
+
reason: 'Same endpoint affected within 5 minutes',
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// Rule 3: Stripe payment failure + server 500 on checkout endpoint within 2 minutes
|
|
32
|
+
if (newEvent.source === 'stripe' || this.isCheckoutEndpoint(newEvent)) {
|
|
33
|
+
const correlated = this.correlatePaymentAndCheckout(newEvent, recentEvents);
|
|
34
|
+
if (correlated) {
|
|
35
|
+
const incident = this.findIncidentWithEvent(correlated.id, openIncidents);
|
|
36
|
+
if (incident) {
|
|
37
|
+
return {
|
|
38
|
+
shouldCorrelate: true,
|
|
39
|
+
correlationGroup: incident.correlation_group || 'payment-checkout-failure',
|
|
40
|
+
existingIncidentId: incident.id,
|
|
41
|
+
reason: 'Payment failure correlated with checkout endpoint error',
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
shouldCorrelate: true,
|
|
46
|
+
correlationGroup: 'payment-checkout-failure',
|
|
47
|
+
reason: 'Payment failure correlated with checkout endpoint error',
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Rule 4: Multiple monitors failing at once (3+ within 2 minutes)
|
|
52
|
+
const multipleFailures = this.checkMultipleMonitorFailures(newEvent, recentEvents);
|
|
53
|
+
if (multipleFailures.length >= 2) {
|
|
54
|
+
// Check if there's already an incident for this burst
|
|
55
|
+
const burstIncident = openIncidents.find((inc) => inc.correlation_group?.startsWith('multi-failure-'));
|
|
56
|
+
if (burstIncident) {
|
|
57
|
+
return {
|
|
58
|
+
shouldCorrelate: true,
|
|
59
|
+
correlationGroup: burstIncident.correlation_group,
|
|
60
|
+
existingIncidentId: burstIncident.id,
|
|
61
|
+
reason: 'Part of multi-monitor failure burst',
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
shouldCorrelate: true,
|
|
66
|
+
correlationGroup: `multi-failure-${Date.now()}`,
|
|
67
|
+
reason: `${multipleFailures.length + 1} monitors failing simultaneously`,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
// No correlation found
|
|
71
|
+
return { shouldCorrelate: false };
|
|
72
|
+
}
|
|
73
|
+
checkProviderOutage(event, providerStatuses) {
|
|
74
|
+
// Map event sources to provider names
|
|
75
|
+
const providerMap = {
|
|
76
|
+
vercel: 'vercel',
|
|
77
|
+
supabase: 'supabase',
|
|
78
|
+
stripe: 'stripe',
|
|
79
|
+
github: 'github',
|
|
80
|
+
};
|
|
81
|
+
const provider = providerMap[event.source];
|
|
82
|
+
if (!provider)
|
|
83
|
+
return null;
|
|
84
|
+
const status = providerStatuses.find((p) => p.provider === provider);
|
|
85
|
+
if (status && (status.status === 'degraded' || status.status === 'outage')) {
|
|
86
|
+
return status;
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
findSameEndpoint(newEvent, recentEvents) {
|
|
91
|
+
const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000);
|
|
92
|
+
const newEndpoint = this.extractEndpoint(newEvent);
|
|
93
|
+
if (!newEndpoint)
|
|
94
|
+
return null;
|
|
95
|
+
return (recentEvents.find((event) => {
|
|
96
|
+
if (event.created_at < fiveMinutesAgo)
|
|
97
|
+
return false;
|
|
98
|
+
if (event.id === newEvent.id)
|
|
99
|
+
return false;
|
|
100
|
+
const endpoint = this.extractEndpoint(event);
|
|
101
|
+
return endpoint === newEndpoint;
|
|
102
|
+
}) || null);
|
|
103
|
+
}
|
|
104
|
+
extractEndpoint(event) {
|
|
105
|
+
// Try to extract URL from monitor or raw_data
|
|
106
|
+
if (event.monitor_id && event.raw_data?.url) {
|
|
107
|
+
return String(event.raw_data.url);
|
|
108
|
+
}
|
|
109
|
+
// Try to extract from message (basic pattern matching)
|
|
110
|
+
const urlMatch = event.message.match(/https?:\/\/[^\s]+/);
|
|
111
|
+
if (urlMatch) {
|
|
112
|
+
return urlMatch[0];
|
|
113
|
+
}
|
|
114
|
+
// Extract path from message like "POST /api/checkout failed"
|
|
115
|
+
const pathMatch = event.message.match(/\/(api|checkout|webhook|auth)\/[^\s]*/);
|
|
116
|
+
if (pathMatch) {
|
|
117
|
+
return pathMatch[0];
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
isCheckoutEndpoint(event) {
|
|
122
|
+
const endpoint = this.extractEndpoint(event);
|
|
123
|
+
if (!endpoint)
|
|
124
|
+
return false;
|
|
125
|
+
return (endpoint.includes('/checkout') ||
|
|
126
|
+
endpoint.includes('/payment') ||
|
|
127
|
+
endpoint.includes('/stripe'));
|
|
128
|
+
}
|
|
129
|
+
correlatePaymentAndCheckout(newEvent, recentEvents) {
|
|
130
|
+
const twoMinutesAgo = new Date(Date.now() - 2 * 60 * 1000);
|
|
131
|
+
// If this is a stripe event, look for recent checkout errors
|
|
132
|
+
if (newEvent.source === 'stripe') {
|
|
133
|
+
return (recentEvents.find((event) => {
|
|
134
|
+
if (event.created_at < twoMinutesAgo)
|
|
135
|
+
return false;
|
|
136
|
+
if (event.source === 'stripe')
|
|
137
|
+
return false;
|
|
138
|
+
return this.isCheckoutEndpoint(event) && event.type === 'error';
|
|
139
|
+
}) || null);
|
|
140
|
+
}
|
|
141
|
+
// If this is a checkout error, look for recent stripe failures
|
|
142
|
+
if (this.isCheckoutEndpoint(newEvent) && newEvent.type === 'error') {
|
|
143
|
+
return (recentEvents.find((event) => {
|
|
144
|
+
if (event.created_at < twoMinutesAgo)
|
|
145
|
+
return false;
|
|
146
|
+
return event.source === 'stripe' && event.type === 'error';
|
|
147
|
+
}) || null);
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
checkMultipleMonitorFailures(newEvent, recentEvents) {
|
|
152
|
+
if (newEvent.type !== 'down' && newEvent.type !== 'error')
|
|
153
|
+
return [];
|
|
154
|
+
const twoMinutesAgo = new Date(Date.now() - 2 * 60 * 1000);
|
|
155
|
+
return recentEvents.filter((event) => {
|
|
156
|
+
if (event.created_at < twoMinutesAgo)
|
|
157
|
+
return false;
|
|
158
|
+
if (event.id === newEvent.id)
|
|
159
|
+
return false;
|
|
160
|
+
if (event.type !== 'down' && event.type !== 'error')
|
|
161
|
+
return false;
|
|
162
|
+
// Must be from different monitors
|
|
163
|
+
if (event.monitor_id && newEvent.monitor_id && event.monitor_id === newEvent.monitor_id) {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
return true;
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
findIncidentWithEvent(eventId, incidents) {
|
|
170
|
+
return incidents.find((inc) => inc.events.includes(eventId)) || null;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
exports.Correlator = Correlator;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Event, Monitor, DiagnosisResult } from './types.js';
|
|
2
|
+
interface DiagnoserConfig {
|
|
3
|
+
apiKey: string;
|
|
4
|
+
model?: string;
|
|
5
|
+
}
|
|
6
|
+
interface DiagnosisContext {
|
|
7
|
+
events: Event[];
|
|
8
|
+
monitor?: Monitor;
|
|
9
|
+
recentHistory?: Array<{
|
|
10
|
+
timestamp: Date;
|
|
11
|
+
status: string;
|
|
12
|
+
message: string;
|
|
13
|
+
}>;
|
|
14
|
+
}
|
|
15
|
+
export declare class Diagnoser {
|
|
16
|
+
private client;
|
|
17
|
+
private model;
|
|
18
|
+
constructor(config: DiagnoserConfig);
|
|
19
|
+
diagnose(context: DiagnosisContext): Promise<DiagnosisResult>;
|
|
20
|
+
private getSystemPrompt;
|
|
21
|
+
private buildPrompt;
|
|
22
|
+
private sanitizeRawData;
|
|
23
|
+
private parseResponse;
|
|
24
|
+
private normalizeSeverity;
|
|
25
|
+
}
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Diagnoser = void 0;
|
|
7
|
+
const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
|
|
8
|
+
class Diagnoser {
|
|
9
|
+
client;
|
|
10
|
+
model;
|
|
11
|
+
constructor(config) {
|
|
12
|
+
this.client = new sdk_1.default({
|
|
13
|
+
apiKey: config.apiKey,
|
|
14
|
+
});
|
|
15
|
+
this.model = config.model || 'claude-sonnet-4-20250514';
|
|
16
|
+
}
|
|
17
|
+
async diagnose(context) {
|
|
18
|
+
const prompt = this.buildPrompt(context);
|
|
19
|
+
const response = await this.client.messages.create({
|
|
20
|
+
model: this.model,
|
|
21
|
+
max_tokens: 2000,
|
|
22
|
+
temperature: 0.3,
|
|
23
|
+
system: this.getSystemPrompt(),
|
|
24
|
+
messages: [
|
|
25
|
+
{
|
|
26
|
+
role: 'user',
|
|
27
|
+
content: prompt,
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
});
|
|
31
|
+
const content = response.content[0];
|
|
32
|
+
if (content.type !== 'text') {
|
|
33
|
+
throw new Error('Unexpected response type from Claude');
|
|
34
|
+
}
|
|
35
|
+
return this.parseResponse(content.text);
|
|
36
|
+
}
|
|
37
|
+
getSystemPrompt() {
|
|
38
|
+
return `You are a senior engineering mentor helping developers who built their application using AI coding tools like Cursor or Claude Code. These developers may not have deep infrastructure knowledge or be familiar with reading stack traces.
|
|
39
|
+
|
|
40
|
+
Your job is to:
|
|
41
|
+
1. Explain what went wrong in plain, conversational English (no jargon)
|
|
42
|
+
2. Explain WHY it happened in a way a non-expert can understand
|
|
43
|
+
3. Provide a clear, actionable fix in plain language
|
|
44
|
+
4. Write a ready-to-paste prompt they can give to their AI coding assistant to fix the issue
|
|
45
|
+
|
|
46
|
+
Think of yourself as a patient mentor who's explaining a production issue to someone smart but new to production systems.
|
|
47
|
+
|
|
48
|
+
IMPORTANT RULES:
|
|
49
|
+
- NO technical jargon without explanation
|
|
50
|
+
- NO raw stack traces in your response
|
|
51
|
+
- Use analogies when helpful
|
|
52
|
+
- Be encouraging, not condescending
|
|
53
|
+
- Focus on "what to do" not "what you did wrong"
|
|
54
|
+
|
|
55
|
+
Respond in this exact JSON format:
|
|
56
|
+
{
|
|
57
|
+
"root_cause": "1-2 sentence plain English explanation of what broke",
|
|
58
|
+
"severity": "critical|warning|info",
|
|
59
|
+
"suggested_fix": "Plain English explanation of how to fix it (2-4 sentences)",
|
|
60
|
+
"fix_prompt": "A complete, copy-pasteable prompt for Cursor/Claude Code that will fix this issue"
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
The fix_prompt should be detailed and include:
|
|
64
|
+
- What file(s) to modify
|
|
65
|
+
- What specific changes to make
|
|
66
|
+
- Any environment variables or config needed
|
|
67
|
+
- How to test the fix
|
|
68
|
+
|
|
69
|
+
Make the fix_prompt actionable enough that an AI coding assistant can implement it without asking follow-up questions.`;
|
|
70
|
+
}
|
|
71
|
+
buildPrompt(context) {
|
|
72
|
+
const { events, monitor, recentHistory } = context;
|
|
73
|
+
let prompt = '## Production Issue Detected\n\n';
|
|
74
|
+
// Add monitor context if available
|
|
75
|
+
if (monitor) {
|
|
76
|
+
prompt += `**Service:** ${monitor.url}\n`;
|
|
77
|
+
prompt += `**Current Status:** ${monitor.status}\n\n`;
|
|
78
|
+
}
|
|
79
|
+
// Add event information
|
|
80
|
+
prompt += `**Recent Events:**\n`;
|
|
81
|
+
for (const event of events) {
|
|
82
|
+
prompt += `- [${event.type.toUpperCase()}] ${event.message}\n`;
|
|
83
|
+
prompt += ` Severity: ${event.severity} | Time: ${event.created_at.toISOString()}\n`;
|
|
84
|
+
if (event.raw_data) {
|
|
85
|
+
const sanitizedData = this.sanitizeRawData(event.raw_data);
|
|
86
|
+
if (Object.keys(sanitizedData).length > 0) {
|
|
87
|
+
prompt += ` Details: ${JSON.stringify(sanitizedData, null, 2)}\n`;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
prompt += '\n';
|
|
91
|
+
}
|
|
92
|
+
// Add recent history if available
|
|
93
|
+
if (recentHistory && recentHistory.length > 0) {
|
|
94
|
+
prompt += `\n**Recent History (last 24 hours):**\n`;
|
|
95
|
+
for (const item of recentHistory.slice(0, 10)) {
|
|
96
|
+
prompt += `- ${item.timestamp.toISOString()}: ${item.status} - ${item.message}\n`;
|
|
97
|
+
}
|
|
98
|
+
prompt += '\n';
|
|
99
|
+
}
|
|
100
|
+
prompt += '\nPlease diagnose this issue and provide a fix.';
|
|
101
|
+
return prompt;
|
|
102
|
+
}
|
|
103
|
+
sanitizeRawData(data) {
|
|
104
|
+
const sanitized = {};
|
|
105
|
+
// Include relevant fields, exclude sensitive or verbose ones
|
|
106
|
+
const relevantFields = [
|
|
107
|
+
'statusCode',
|
|
108
|
+
'responseTime',
|
|
109
|
+
'error',
|
|
110
|
+
'url',
|
|
111
|
+
'method',
|
|
112
|
+
'level',
|
|
113
|
+
'message',
|
|
114
|
+
'type',
|
|
115
|
+
'source',
|
|
116
|
+
];
|
|
117
|
+
for (const field of relevantFields) {
|
|
118
|
+
if (field in data) {
|
|
119
|
+
sanitized[field] = data[field];
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return sanitized;
|
|
123
|
+
}
|
|
124
|
+
parseResponse(text) {
|
|
125
|
+
try {
|
|
126
|
+
// Try to extract JSON from the response
|
|
127
|
+
const jsonMatch = text.match(/\{[\s\S]*\}/);
|
|
128
|
+
if (!jsonMatch) {
|
|
129
|
+
throw new Error('No JSON found in response');
|
|
130
|
+
}
|
|
131
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
132
|
+
return {
|
|
133
|
+
root_cause: parsed.root_cause || 'Unable to determine root cause',
|
|
134
|
+
severity: this.normalizeSeverity(parsed.severity),
|
|
135
|
+
suggested_fix: parsed.suggested_fix || 'No fix suggested',
|
|
136
|
+
fix_prompt: parsed.fix_prompt || 'No fix prompt provided',
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
// Fallback if parsing fails
|
|
141
|
+
console.error('Failed to parse diagnosis response:', error);
|
|
142
|
+
return {
|
|
143
|
+
root_cause: 'Failed to parse diagnosis from AI response',
|
|
144
|
+
severity: 'warning',
|
|
145
|
+
suggested_fix: text.substring(0, 500),
|
|
146
|
+
fix_prompt: 'Unable to generate fix prompt. Please review the raw diagnosis and consult your AI coding assistant.',
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
normalizeSeverity(severity) {
|
|
151
|
+
const normalized = severity.toLowerCase();
|
|
152
|
+
if (normalized === 'critical')
|
|
153
|
+
return 'critical';
|
|
154
|
+
if (normalized === 'warning')
|
|
155
|
+
return 'warning';
|
|
156
|
+
return 'info';
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
exports.Diagnoser = Diagnoser;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./types.js"), exports);
|
|
18
|
+
__exportStar(require("./diagnoser.js"), exports);
|
|
19
|
+
__exportStar(require("./correlator.js"), exports);
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
export interface Monitor {
|
|
2
|
+
id: string;
|
|
3
|
+
project_id: string;
|
|
4
|
+
url: string;
|
|
5
|
+
check_interval_seconds: number;
|
|
6
|
+
last_checked_at?: Date;
|
|
7
|
+
status: 'up' | 'down' | 'unknown';
|
|
8
|
+
created_at: Date;
|
|
9
|
+
}
|
|
10
|
+
export type EventSource = 'monitor' | 'vercel' | 'stripe' | 'supabase' | 'github' | 'provider-status';
|
|
11
|
+
export interface Event {
|
|
12
|
+
id: string;
|
|
13
|
+
project_id: string;
|
|
14
|
+
monitor_id?: string;
|
|
15
|
+
type: 'error' | 'slow' | 'down' | 'up';
|
|
16
|
+
source: EventSource;
|
|
17
|
+
message: string;
|
|
18
|
+
raw_data?: Record<string, unknown>;
|
|
19
|
+
severity: 'low' | 'medium' | 'high' | 'critical';
|
|
20
|
+
created_at: Date;
|
|
21
|
+
}
|
|
22
|
+
export interface EventStats {
|
|
23
|
+
monitor_id: string;
|
|
24
|
+
avg_response_time?: number;
|
|
25
|
+
total_checks: number;
|
|
26
|
+
error_count: number;
|
|
27
|
+
last_error_at?: Date;
|
|
28
|
+
updated_at: Date;
|
|
29
|
+
}
|
|
30
|
+
export interface VercelLogDrainPayload {
|
|
31
|
+
source: string;
|
|
32
|
+
deploymentId: string;
|
|
33
|
+
message: string;
|
|
34
|
+
timestamp: number;
|
|
35
|
+
type: 'stdout' | 'stderr' | 'request' | 'response';
|
|
36
|
+
level?: 'info' | 'warn' | 'error' | 'debug';
|
|
37
|
+
[key: string]: unknown;
|
|
38
|
+
}
|
|
39
|
+
export interface WebhookPayload {
|
|
40
|
+
event: string;
|
|
41
|
+
service: string;
|
|
42
|
+
data: Record<string, unknown>;
|
|
43
|
+
timestamp: string;
|
|
44
|
+
}
|
|
45
|
+
export interface Incident {
|
|
46
|
+
id: string;
|
|
47
|
+
project_id: string;
|
|
48
|
+
events: string[];
|
|
49
|
+
correlation_group?: string;
|
|
50
|
+
status: 'open' | 'investigating' | 'resolved';
|
|
51
|
+
diagnosis_text?: string;
|
|
52
|
+
diagnosis_fix?: string;
|
|
53
|
+
severity: 'critical' | 'warning' | 'info';
|
|
54
|
+
fix_prompt?: string;
|
|
55
|
+
created_at: Date;
|
|
56
|
+
resolved_at?: Date;
|
|
57
|
+
}
|
|
58
|
+
export interface DiagnosisResult {
|
|
59
|
+
root_cause: string;
|
|
60
|
+
severity: 'critical' | 'warning' | 'info';
|
|
61
|
+
suggested_fix: string;
|
|
62
|
+
fix_prompt: string;
|
|
63
|
+
}
|
|
64
|
+
export interface ProviderStatus {
|
|
65
|
+
provider: string;
|
|
66
|
+
status: 'operational' | 'degraded' | 'outage';
|
|
67
|
+
last_checked_at: Date;
|
|
68
|
+
details?: string;
|
|
69
|
+
}
|
|
70
|
+
export interface StripeWebhookEvent {
|
|
71
|
+
id: string;
|
|
72
|
+
type: string;
|
|
73
|
+
data: {
|
|
74
|
+
object: Record<string, unknown>;
|
|
75
|
+
};
|
|
76
|
+
[key: string]: unknown;
|
|
77
|
+
}
|
|
78
|
+
export interface GitHubWebhookEvent {
|
|
79
|
+
action?: string;
|
|
80
|
+
workflow_run?: {
|
|
81
|
+
conclusion: string;
|
|
82
|
+
name: string;
|
|
83
|
+
html_url: string;
|
|
84
|
+
};
|
|
85
|
+
alert?: {
|
|
86
|
+
number: number;
|
|
87
|
+
state: string;
|
|
88
|
+
html_url: string;
|
|
89
|
+
};
|
|
90
|
+
[key: string]: unknown;
|
|
91
|
+
}
|
|
92
|
+
export interface ProviderEvent {
|
|
93
|
+
source: EventSource;
|
|
94
|
+
type: Event['type'];
|
|
95
|
+
message: string;
|
|
96
|
+
severity: Event['severity'];
|
|
97
|
+
raw_data: Record<string, unknown>;
|
|
98
|
+
}
|
|
99
|
+
export interface MonitoringConfig {
|
|
100
|
+
id: string;
|
|
101
|
+
serviceName: string;
|
|
102
|
+
endpoint: string;
|
|
103
|
+
interval: number;
|
|
104
|
+
createdAt: Date;
|
|
105
|
+
updatedAt: Date;
|
|
106
|
+
}
|
|
107
|
+
export interface MonitoringEvent {
|
|
108
|
+
id: string;
|
|
109
|
+
configId: string;
|
|
110
|
+
status: 'success' | 'failure';
|
|
111
|
+
responseTime?: number;
|
|
112
|
+
statusCode?: number;
|
|
113
|
+
errorMessage?: string;
|
|
114
|
+
timestamp: Date;
|
|
115
|
+
}
|
package/dist/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@scanwarp/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Shared types and logic for ScanWarp monitoring",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsc --watch",
|
|
13
|
+
"typecheck": "tsc --noEmit",
|
|
14
|
+
"clean": "rm -rf dist",
|
|
15
|
+
"prepublishOnly": "npm run build"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"monitoring",
|
|
19
|
+
"observability",
|
|
20
|
+
"ai",
|
|
21
|
+
"claude",
|
|
22
|
+
"diagnosis",
|
|
23
|
+
"production",
|
|
24
|
+
"incidents"
|
|
25
|
+
],
|
|
26
|
+
"author": "ScanWarp",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/scanwarp/scanwarp.git",
|
|
31
|
+
"directory": "packages/core"
|
|
32
|
+
},
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/scanwarp/scanwarp/issues"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://scanwarp.com",
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/node": "^20.11.0",
|
|
39
|
+
"typescript": "^5.3.3"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@anthropic-ai/sdk": "^0.74.0"
|
|
43
|
+
}
|
|
44
|
+
}
|