catalist-support-agent 1.0.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/admin-portal.d.ts +43 -0
- package/dist/admin-portal.d.ts.map +1 -0
- package/dist/admin-portal.js +166 -0
- package/dist/admin-portal.js.map +1 -0
- package/dist/analysis/entities.d.ts +73 -0
- package/dist/analysis/entities.d.ts.map +1 -0
- package/dist/analysis/entities.js +378 -0
- package/dist/analysis/entities.js.map +1 -0
- package/dist/analysis/index.d.ts +44 -0
- package/dist/analysis/index.d.ts.map +1 -0
- package/dist/analysis/index.js +243 -0
- package/dist/analysis/index.js.map +1 -0
- package/dist/analysis/intent.d.ts +49 -0
- package/dist/analysis/intent.d.ts.map +1 -0
- package/dist/analysis/intent.js +320 -0
- package/dist/analysis/intent.js.map +1 -0
- package/dist/analysis/sentiment.d.ts +57 -0
- package/dist/analysis/sentiment.d.ts.map +1 -0
- package/dist/analysis/sentiment.js +351 -0
- package/dist/analysis/sentiment.js.map +1 -0
- package/dist/brand/compliance.d.ts +122 -0
- package/dist/brand/compliance.d.ts.map +1 -0
- package/dist/brand/compliance.js +378 -0
- package/dist/brand/compliance.js.map +1 -0
- package/dist/brand/forbidden-terms.d.ts +99 -0
- package/dist/brand/forbidden-terms.d.ts.map +1 -0
- package/dist/brand/forbidden-terms.js +265 -0
- package/dist/brand/forbidden-terms.js.map +1 -0
- package/dist/brand/index.d.ts +10 -0
- package/dist/brand/index.d.ts.map +1 -0
- package/dist/brand/index.js +12 -0
- package/dist/brand/index.js.map +1 -0
- package/dist/config.d.ts +325 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +492 -0
- package/dist/config.js.map +1 -0
- package/dist/delivery/index.d.ts +84 -0
- package/dist/delivery/index.d.ts.map +1 -0
- package/dist/delivery/index.js +435 -0
- package/dist/delivery/index.js.map +1 -0
- package/dist/embeddings/cache.d.ts +96 -0
- package/dist/embeddings/cache.d.ts.map +1 -0
- package/dist/embeddings/cache.js +193 -0
- package/dist/embeddings/cache.js.map +1 -0
- package/dist/embeddings/index.d.ts +152 -0
- package/dist/embeddings/index.d.ts.map +1 -0
- package/dist/embeddings/index.js +337 -0
- package/dist/embeddings/index.js.map +1 -0
- package/dist/embeddings/openai-client.d.ts +67 -0
- package/dist/embeddings/openai-client.d.ts.map +1 -0
- package/dist/embeddings/openai-client.js +190 -0
- package/dist/embeddings/openai-client.js.map +1 -0
- package/dist/errors.d.ts +302 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +508 -0
- package/dist/errors.js.map +1 -0
- package/dist/escalation/index.d.ts +93 -0
- package/dist/escalation/index.d.ts.map +1 -0
- package/dist/escalation/index.js +436 -0
- package/dist/escalation/index.js.map +1 -0
- package/dist/extraction/deduplication.d.ts +97 -0
- package/dist/extraction/deduplication.d.ts.map +1 -0
- package/dist/extraction/deduplication.js +271 -0
- package/dist/extraction/deduplication.js.map +1 -0
- package/dist/extraction/gmail-extractor.d.ts +160 -0
- package/dist/extraction/gmail-extractor.d.ts.map +1 -0
- package/dist/extraction/gmail-extractor.js +396 -0
- package/dist/extraction/gmail-extractor.js.map +1 -0
- package/dist/extraction/gmail-token-manager.d.ts +36 -0
- package/dist/extraction/gmail-token-manager.d.ts.map +1 -0
- package/dist/extraction/gmail-token-manager.js +146 -0
- package/dist/extraction/gmail-token-manager.js.map +1 -0
- package/dist/extraction/index.d.ts +13 -0
- package/dist/extraction/index.d.ts.map +1 -0
- package/dist/extraction/index.js +20 -0
- package/dist/extraction/index.js.map +1 -0
- package/dist/extraction/pii-handler.d.ts +100 -0
- package/dist/extraction/pii-handler.d.ts.map +1 -0
- package/dist/extraction/pii-handler.js +295 -0
- package/dist/extraction/pii-handler.js.map +1 -0
- package/dist/extraction/pipeline.d.ts +94 -0
- package/dist/extraction/pipeline.d.ts.map +1 -0
- package/dist/extraction/pipeline.js +380 -0
- package/dist/extraction/pipeline.js.map +1 -0
- package/dist/extraction/quality-filter.d.ts +99 -0
- package/dist/extraction/quality-filter.d.ts.map +1 -0
- package/dist/extraction/quality-filter.js +370 -0
- package/dist/extraction/quality-filter.js.map +1 -0
- package/dist/extraction/rate-limiter.d.ts +90 -0
- package/dist/extraction/rate-limiter.d.ts.map +1 -0
- package/dist/extraction/rate-limiter.js +242 -0
- package/dist/extraction/rate-limiter.js.map +1 -0
- package/dist/extraction/state-manager.d.ts +126 -0
- package/dist/extraction/state-manager.d.ts.map +1 -0
- package/dist/extraction/state-manager.js +344 -0
- package/dist/extraction/state-manager.js.map +1 -0
- package/dist/generation/index.d.ts +75 -0
- package/dist/generation/index.d.ts.map +1 -0
- package/dist/generation/index.js +641 -0
- package/dist/generation/index.js.map +1 -0
- package/dist/index.d.ts +96 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +233 -0
- package/dist/index.js.map +1 -0
- package/dist/intake/index.d.ts +15 -0
- package/dist/intake/index.d.ts.map +1 -0
- package/dist/intake/index.js +19 -0
- package/dist/intake/index.js.map +1 -0
- package/dist/intake/normalizer.d.ts +163 -0
- package/dist/intake/normalizer.d.ts.map +1 -0
- package/dist/intake/normalizer.js +309 -0
- package/dist/intake/normalizer.js.map +1 -0
- package/dist/intake/postmark.d.ts +72 -0
- package/dist/intake/postmark.d.ts.map +1 -0
- package/dist/intake/postmark.js +276 -0
- package/dist/intake/postmark.js.map +1 -0
- package/dist/intake/slack.d.ts +106 -0
- package/dist/intake/slack.d.ts.map +1 -0
- package/dist/intake/slack.js +378 -0
- package/dist/intake/slack.js.map +1 -0
- package/dist/intake/twilio.d.ts +86 -0
- package/dist/intake/twilio.d.ts.map +1 -0
- package/dist/intake/twilio.js +283 -0
- package/dist/intake/twilio.js.map +1 -0
- package/dist/knowledge/index.d.ts +100 -0
- package/dist/knowledge/index.d.ts.map +1 -0
- package/dist/knowledge/index.js +516 -0
- package/dist/knowledge/index.js.map +1 -0
- package/dist/knowledge/invoice-resolver.d.ts +62 -0
- package/dist/knowledge/invoice-resolver.d.ts.map +1 -0
- package/dist/knowledge/invoice-resolver.js +267 -0
- package/dist/knowledge/invoice-resolver.js.map +1 -0
- package/dist/types.d.ts +535 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +48 -0
- package/dist/types.js.map +1 -0
- package/ga-service-account.json +13 -0
- package/gmail-knowledge-migration.sql +149 -0
- package/nul +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gmail Extractor Module
|
|
3
|
+
*
|
|
4
|
+
* Extracts Q&A pairs from Gmail threads for the knowledge base.
|
|
5
|
+
* Uses Gmail API to fetch threads and parses them to identify
|
|
6
|
+
* inbound questions and outbound responses.
|
|
7
|
+
*/
|
|
8
|
+
import { createClient } from '@supabase/supabase-js';
|
|
9
|
+
import { PIIHandler } from './pii-handler.js';
|
|
10
|
+
import { QualityFilter, inferResolutionIndicator } from './quality-filter.js';
|
|
11
|
+
import { DeduplicationService } from './deduplication.js';
|
|
12
|
+
// =============================================================================
|
|
13
|
+
// Gmail Extractor
|
|
14
|
+
// =============================================================================
|
|
15
|
+
export class GmailExtractor {
|
|
16
|
+
supabase;
|
|
17
|
+
piiHandler;
|
|
18
|
+
qualityFilter;
|
|
19
|
+
deduplicationService;
|
|
20
|
+
salesEmail;
|
|
21
|
+
constructor(config) {
|
|
22
|
+
this.supabase = createClient(config.supabaseUrl, config.supabaseServiceRoleKey);
|
|
23
|
+
this.salesEmail = config.salesEmailAddress.toLowerCase();
|
|
24
|
+
this.piiHandler = new PIIHandler({
|
|
25
|
+
enabled: true,
|
|
26
|
+
preserveCompanyEmails: config.preserveEmailDomains || ['@catalistgroup.co', '@catalist.deals'],
|
|
27
|
+
});
|
|
28
|
+
this.qualityFilter = new QualityFilter();
|
|
29
|
+
this.deduplicationService = new DeduplicationService({
|
|
30
|
+
supabaseUrl: config.supabaseUrl,
|
|
31
|
+
supabaseServiceRoleKey: config.supabaseServiceRoleKey,
|
|
32
|
+
similarityThreshold: 0.95,
|
|
33
|
+
useEmbeddingSimilarity: false,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Extract Q&A pairs from a list of Gmail threads
|
|
38
|
+
*/
|
|
39
|
+
async extractFromThreads(threads) {
|
|
40
|
+
const result = {
|
|
41
|
+
extracted: [],
|
|
42
|
+
skipped: [],
|
|
43
|
+
stats: {
|
|
44
|
+
threadsProcessed: 0,
|
|
45
|
+
messagesProcessed: 0,
|
|
46
|
+
pairsExtracted: 0,
|
|
47
|
+
duplicatesSkipped: 0,
|
|
48
|
+
qualityFilteredOut: 0,
|
|
49
|
+
errors: 0,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
// Load existing hashes for deduplication
|
|
53
|
+
await this.deduplicationService.loadHashCache();
|
|
54
|
+
for (const thread of threads) {
|
|
55
|
+
try {
|
|
56
|
+
result.stats.threadsProcessed++;
|
|
57
|
+
const pairs = await this.extractFromThread(thread);
|
|
58
|
+
for (const pair of pairs) {
|
|
59
|
+
result.stats.messagesProcessed++;
|
|
60
|
+
// Check for duplicates
|
|
61
|
+
const dedupResult = await this.deduplicationService.checkDuplicate({
|
|
62
|
+
questionText: pair.questionText,
|
|
63
|
+
responseText: pair.responseText,
|
|
64
|
+
questionSubject: pair.questionSubject || undefined,
|
|
65
|
+
});
|
|
66
|
+
if (dedupResult.isDuplicate) {
|
|
67
|
+
result.stats.duplicatesSkipped++;
|
|
68
|
+
result.skipped.push({
|
|
69
|
+
reason: `Duplicate (${dedupResult.method})`,
|
|
70
|
+
threadId: pair.gmailThreadId,
|
|
71
|
+
messageId: pair.gmailMessageId,
|
|
72
|
+
});
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
// Quality filter
|
|
76
|
+
const qualityAssessment = this.qualityFilter.assess({
|
|
77
|
+
questionSubject: pair.questionSubject || undefined,
|
|
78
|
+
questionText: pair.questionText,
|
|
79
|
+
responseText: pair.responseText,
|
|
80
|
+
responseTimeMs: pair.responseTimeMs,
|
|
81
|
+
resolutionIndicator: pair.resolutionIndicator || undefined,
|
|
82
|
+
});
|
|
83
|
+
if (!qualityAssessment.passed) {
|
|
84
|
+
result.stats.qualityFilteredOut++;
|
|
85
|
+
result.skipped.push({
|
|
86
|
+
reason: `Quality filter: ${qualityAssessment.summary}`,
|
|
87
|
+
threadId: pair.gmailThreadId,
|
|
88
|
+
messageId: pair.gmailMessageId,
|
|
89
|
+
});
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
// Update pair with quality score and content hash
|
|
93
|
+
pair.qualityScore = qualityAssessment.score;
|
|
94
|
+
pair.contentHash = dedupResult.contentHash;
|
|
95
|
+
// Add to cache for batch deduplication
|
|
96
|
+
this.deduplicationService.addToCache(dedupResult.contentHash);
|
|
97
|
+
result.extracted.push(pair);
|
|
98
|
+
result.stats.pairsExtracted++;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
result.stats.errors++;
|
|
103
|
+
result.skipped.push({
|
|
104
|
+
reason: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
105
|
+
threadId: thread.id,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Extract Q&A pairs from a single thread
|
|
113
|
+
*/
|
|
114
|
+
async extractFromThread(thread) {
|
|
115
|
+
const pairs = [];
|
|
116
|
+
if (!thread.messages || thread.messages.length < 2) {
|
|
117
|
+
// Need at least 2 messages for a Q&A pair
|
|
118
|
+
return pairs;
|
|
119
|
+
}
|
|
120
|
+
// Sort messages by date (oldest first)
|
|
121
|
+
const sortedMessages = [...thread.messages].sort((a, b) => parseInt(a.internalDate) - parseInt(b.internalDate));
|
|
122
|
+
// Find Q&A pairs: inbound followed by outbound
|
|
123
|
+
for (let i = 0; i < sortedMessages.length - 1; i++) {
|
|
124
|
+
const questionMessage = sortedMessages[i];
|
|
125
|
+
const responseMessage = sortedMessages[i + 1];
|
|
126
|
+
// Skip if messages are undefined (shouldn't happen with valid indices)
|
|
127
|
+
if (!questionMessage || !responseMessage) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
// Check if this is an inbound-outbound pair
|
|
131
|
+
const questionFrom = this.getHeader(questionMessage, 'From');
|
|
132
|
+
const responseFrom = this.getHeader(responseMessage, 'From');
|
|
133
|
+
if (!questionFrom || !responseFrom) {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const questionEmail = this.extractEmail(questionFrom);
|
|
137
|
+
const responseEmail = this.extractEmail(responseFrom);
|
|
138
|
+
// Question must be FROM customer (not our sales email)
|
|
139
|
+
// Response must be FROM our sales email
|
|
140
|
+
if (this.isCompanyEmail(questionEmail) || !this.isCompanyEmail(responseEmail)) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
// Check that response is actually replying to the question
|
|
144
|
+
const inReplyTo = this.getHeader(responseMessage, 'In-Reply-To');
|
|
145
|
+
const references = this.getHeader(responseMessage, 'References');
|
|
146
|
+
// Either In-Reply-To or References should contain the question message ID
|
|
147
|
+
const questionMessageIdHeader = this.getHeader(questionMessage, 'Message-ID');
|
|
148
|
+
const isReply = (inReplyTo && questionMessageIdHeader && inReplyTo.includes(questionMessageIdHeader)) ||
|
|
149
|
+
(references && questionMessageIdHeader && references.includes(questionMessageIdHeader)) ||
|
|
150
|
+
// Fallback: same thread and sequential
|
|
151
|
+
true;
|
|
152
|
+
if (!isReply) {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
// Extract content
|
|
156
|
+
const questionSubject = this.getHeader(questionMessage, 'Subject');
|
|
157
|
+
const questionBody = this.extractBody(questionMessage);
|
|
158
|
+
const responseBody = this.extractBody(responseMessage);
|
|
159
|
+
if (!questionBody || !responseBody) {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
// Apply PII redaction
|
|
163
|
+
const questionPII = this.piiHandler.redact(questionBody);
|
|
164
|
+
const responsePII = this.piiHandler.redact(responseBody);
|
|
165
|
+
// Calculate response time
|
|
166
|
+
const questionDate = new Date(parseInt(questionMessage.internalDate));
|
|
167
|
+
const responseDate = new Date(parseInt(responseMessage.internalDate));
|
|
168
|
+
const responseTimeMs = responseDate.getTime() - questionDate.getTime();
|
|
169
|
+
// Infer resolution indicator
|
|
170
|
+
const resolutionIndicator = inferResolutionIndicator(questionPII.text, responsePII.text);
|
|
171
|
+
pairs.push({
|
|
172
|
+
gmailThreadId: thread.id,
|
|
173
|
+
gmailMessageId: responseMessage.id, // Use response message ID as the identifier
|
|
174
|
+
questionSubject: questionSubject ? this.cleanSubject(questionSubject) : null,
|
|
175
|
+
questionText: questionPII.text,
|
|
176
|
+
responseText: responsePII.text,
|
|
177
|
+
customerEmail: questionEmail,
|
|
178
|
+
responderEmail: responseEmail,
|
|
179
|
+
emailReceivedAt: questionDate,
|
|
180
|
+
emailRespondedAt: responseDate,
|
|
181
|
+
responseTimeMs,
|
|
182
|
+
intentCategory: null, // Will be classified separately
|
|
183
|
+
qualityScore: 0, // Will be calculated by quality filter
|
|
184
|
+
contentHash: '', // Will be set during deduplication
|
|
185
|
+
piiRedacted: questionPII.redacted || responsePII.redacted,
|
|
186
|
+
piiTypesFound: [
|
|
187
|
+
...new Set([...questionPII.piiTypesFound, ...responsePII.piiTypesFound]),
|
|
188
|
+
],
|
|
189
|
+
resolutionIndicator,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
return pairs;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Get a header value from a Gmail message
|
|
196
|
+
*/
|
|
197
|
+
getHeader(message, headerName) {
|
|
198
|
+
const header = message.payload.headers.find((h) => h.name.toLowerCase() === headerName.toLowerCase());
|
|
199
|
+
return header?.value || null;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Extract email address from a From/To header value
|
|
203
|
+
*/
|
|
204
|
+
extractEmail(headerValue) {
|
|
205
|
+
// Format: "Name <email@example.com>" or just "email@example.com"
|
|
206
|
+
const emailMatch = headerValue.match(/<([^>]+)>/) || headerValue.match(/([^\s]+@[^\s]+)/);
|
|
207
|
+
const extracted = emailMatch?.[1];
|
|
208
|
+
return extracted ? extracted.toLowerCase() : headerValue.toLowerCase();
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Check if an email is from the company
|
|
212
|
+
*/
|
|
213
|
+
isCompanyEmail(email) {
|
|
214
|
+
return (email === this.salesEmail ||
|
|
215
|
+
email.endsWith('@catalistgroup.co') ||
|
|
216
|
+
email.endsWith('@catalist.deals'));
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Extract plain text body from a Gmail message
|
|
220
|
+
*/
|
|
221
|
+
extractBody(message) {
|
|
222
|
+
// Try to get plain text body
|
|
223
|
+
const plainText = this.findBodyPart(message.payload, 'text/plain');
|
|
224
|
+
if (plainText) {
|
|
225
|
+
return this.cleanEmailBody(plainText);
|
|
226
|
+
}
|
|
227
|
+
// Fall back to HTML (stripped of tags)
|
|
228
|
+
const html = this.findBodyPart(message.payload, 'text/html');
|
|
229
|
+
if (html) {
|
|
230
|
+
return this.cleanEmailBody(this.stripHtml(html));
|
|
231
|
+
}
|
|
232
|
+
// Try snippet as last resort
|
|
233
|
+
if (message.snippet) {
|
|
234
|
+
return message.snippet;
|
|
235
|
+
}
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Find body part with specified MIME type
|
|
240
|
+
*/
|
|
241
|
+
findBodyPart(payload, mimeType) {
|
|
242
|
+
// Check direct body
|
|
243
|
+
if (payload.mimeType === mimeType && payload.body?.data) {
|
|
244
|
+
return this.decodeBase64Url(payload.body.data);
|
|
245
|
+
}
|
|
246
|
+
// Check parts recursively
|
|
247
|
+
if (payload.parts) {
|
|
248
|
+
for (const part of payload.parts) {
|
|
249
|
+
if (part.mimeType === mimeType && part.body?.data) {
|
|
250
|
+
return this.decodeBase64Url(part.body.data);
|
|
251
|
+
}
|
|
252
|
+
// Check nested parts
|
|
253
|
+
if (part.parts) {
|
|
254
|
+
for (const nestedPart of part.parts) {
|
|
255
|
+
if (nestedPart.mimeType === mimeType && nestedPart.body?.data) {
|
|
256
|
+
return this.decodeBase64Url(nestedPart.body.data);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Decode base64url encoded string
|
|
266
|
+
*/
|
|
267
|
+
decodeBase64Url(encoded) {
|
|
268
|
+
// Replace URL-safe characters
|
|
269
|
+
const base64 = encoded.replace(/-/g, '+').replace(/_/g, '/');
|
|
270
|
+
return Buffer.from(base64, 'base64').toString('utf-8');
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Strip HTML tags from string
|
|
274
|
+
*/
|
|
275
|
+
stripHtml(html) {
|
|
276
|
+
return html
|
|
277
|
+
.replace(/<style[^>]*>.*?<\/style>/gis, '')
|
|
278
|
+
.replace(/<script[^>]*>.*?<\/script>/gis, '')
|
|
279
|
+
.replace(/<[^>]+>/g, ' ')
|
|
280
|
+
.replace(/ /g, ' ')
|
|
281
|
+
.replace(/&/g, '&')
|
|
282
|
+
.replace(/</g, '<')
|
|
283
|
+
.replace(/>/g, '>')
|
|
284
|
+
.replace(/"/g, '"')
|
|
285
|
+
.replace(/'/g, "'")
|
|
286
|
+
.replace(/\s+/g, ' ')
|
|
287
|
+
.trim();
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Clean email body (remove signatures, quoted replies, etc.)
|
|
291
|
+
*/
|
|
292
|
+
cleanEmailBody(body) {
|
|
293
|
+
let cleaned = body;
|
|
294
|
+
// Remove common reply markers and everything after
|
|
295
|
+
const replyMarkers = [
|
|
296
|
+
/^--\s*$/m, // Standard signature delimiter
|
|
297
|
+
/^_{5,}/m, // Underscore line
|
|
298
|
+
/^-{5,}/m, // Dash line
|
|
299
|
+
/On .+ wrote:/im, // Gmail quote
|
|
300
|
+
/On .+,.*wrote:/im, // Alternative quote format
|
|
301
|
+
/From:.*\nSent:.*\nTo:/im, // Outlook quote
|
|
302
|
+
/-----Original Message-----/i,
|
|
303
|
+
/^>+/m, // Quote markers
|
|
304
|
+
/\[cid:image.*\]/gi, // Inline image placeholders
|
|
305
|
+
/Sent from my iPhone/i,
|
|
306
|
+
/Sent from my Android/i,
|
|
307
|
+
/Get Outlook for/i,
|
|
308
|
+
];
|
|
309
|
+
for (const marker of replyMarkers) {
|
|
310
|
+
const match = cleaned.match(marker);
|
|
311
|
+
if (match && match.index !== undefined) {
|
|
312
|
+
cleaned = cleaned.substring(0, match.index);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
// Remove email footer patterns
|
|
316
|
+
cleaned = cleaned
|
|
317
|
+
.replace(/\[https?:\/\/[^\]]+\]/g, '') // Remove bracketed URLs
|
|
318
|
+
.replace(/\s+/g, ' ') // Collapse whitespace
|
|
319
|
+
.trim();
|
|
320
|
+
return cleaned;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Clean subject line (remove Re:, Fwd:, etc.)
|
|
324
|
+
*/
|
|
325
|
+
cleanSubject(subject) {
|
|
326
|
+
return subject
|
|
327
|
+
.replace(/^(Re:\s*|Fwd:\s*|Fw:\s*)+/gi, '')
|
|
328
|
+
.trim();
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Store extracted pairs in the database
|
|
332
|
+
*/
|
|
333
|
+
async storeExtractedPairs(pairs, batchId) {
|
|
334
|
+
let stored = 0;
|
|
335
|
+
let errors = 0;
|
|
336
|
+
for (const pair of pairs) {
|
|
337
|
+
try {
|
|
338
|
+
const { error } = await this.supabase.from('gmail_knowledge_entries').insert({
|
|
339
|
+
gmail_thread_id: pair.gmailThreadId,
|
|
340
|
+
gmail_message_id: pair.gmailMessageId,
|
|
341
|
+
question_subject: pair.questionSubject,
|
|
342
|
+
question_text: pair.questionText,
|
|
343
|
+
response_text: pair.responseText,
|
|
344
|
+
customer_email: pair.customerEmail,
|
|
345
|
+
responder_email: pair.responderEmail,
|
|
346
|
+
intent_category: pair.intentCategory,
|
|
347
|
+
quality_score: pair.qualityScore,
|
|
348
|
+
review_status: 'pending',
|
|
349
|
+
content_hash: pair.contentHash,
|
|
350
|
+
pii_redacted: pair.piiRedacted,
|
|
351
|
+
pii_types_found: pair.piiTypesFound,
|
|
352
|
+
resolution_indicator: pair.resolutionIndicator,
|
|
353
|
+
response_time_ms: pair.responseTimeMs,
|
|
354
|
+
email_received_at: pair.emailReceivedAt.toISOString(),
|
|
355
|
+
email_responded_at: pair.emailRespondedAt.toISOString(),
|
|
356
|
+
batch_id: batchId,
|
|
357
|
+
});
|
|
358
|
+
if (error) {
|
|
359
|
+
console.error(`Error storing pair ${pair.gmailMessageId}:`, error.message);
|
|
360
|
+
errors++;
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
stored++;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
catch (err) {
|
|
367
|
+
console.error(`Exception storing pair ${pair.gmailMessageId}:`, err);
|
|
368
|
+
errors++;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return { stored, errors };
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Get deduplication service for external use
|
|
375
|
+
*/
|
|
376
|
+
getDeduplicationService() {
|
|
377
|
+
return this.deduplicationService;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Get quality filter for external use
|
|
381
|
+
*/
|
|
382
|
+
getQualityFilter() {
|
|
383
|
+
return this.qualityFilter;
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Get PII handler for external use
|
|
387
|
+
*/
|
|
388
|
+
getPIIHandler() {
|
|
389
|
+
return this.piiHandler;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
// =============================================================================
|
|
393
|
+
// Exports
|
|
394
|
+
// =============================================================================
|
|
395
|
+
export { PIIHandler, QualityFilter, DeduplicationService };
|
|
396
|
+
//# sourceMappingURL=gmail-extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gmail-extractor.js","sourceRoot":"","sources":["../../src/extraction/gmail-extractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gmail Token Manager
|
|
3
|
+
*
|
|
4
|
+
* Fetches and manages Gmail OAuth access tokens from the database.
|
|
5
|
+
* Handles automatic token refresh when expired.
|
|
6
|
+
*/
|
|
7
|
+
interface TokenManagerConfig {
|
|
8
|
+
supabaseUrl: string;
|
|
9
|
+
supabaseServiceRoleKey: string;
|
|
10
|
+
clientId?: string;
|
|
11
|
+
clientSecret?: string;
|
|
12
|
+
encryptionKey?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare class GmailTokenManager {
|
|
15
|
+
private supabase;
|
|
16
|
+
private clientId;
|
|
17
|
+
private clientSecret;
|
|
18
|
+
private encryptionKey;
|
|
19
|
+
constructor(config: TokenManagerConfig);
|
|
20
|
+
/**
|
|
21
|
+
* Get a valid access token for the given email address.
|
|
22
|
+
* Automatically refreshes the token if expired.
|
|
23
|
+
*/
|
|
24
|
+
getAccessToken(emailAddress: string): Promise<string>;
|
|
25
|
+
/**
|
|
26
|
+
* Refresh the access token using the refresh token
|
|
27
|
+
*/
|
|
28
|
+
private refreshAndStoreToken;
|
|
29
|
+
/**
|
|
30
|
+
* Check if a token exists for the given email address
|
|
31
|
+
*/
|
|
32
|
+
hasToken(emailAddress: string): Promise<boolean>;
|
|
33
|
+
}
|
|
34
|
+
export declare function getGmailTokenManager(config?: Partial<TokenManagerConfig>): GmailTokenManager;
|
|
35
|
+
export {};
|
|
36
|
+
//# sourceMappingURL=gmail-token-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gmail-token-manager.d.ts","sourceRoot":"","sources":["../../src/extraction/gmail-token-manager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAoBH,UAAU,kBAAkB;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AA0BD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,aAAa,CAAS;gBAElB,MAAM,EAAE,kBAAkB;IAOtC;;;OAGG;IACG,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA6B3D;;OAEG;YACW,oBAAoB;IAkDlC;;OAEG;IACG,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAUvD;AAOD,wBAAgB,oBAAoB,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,iBAAiB,CAiB5F"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gmail Token Manager
|
|
3
|
+
*
|
|
4
|
+
* Fetches and manages Gmail OAuth access tokens from the database.
|
|
5
|
+
* Handles automatic token refresh when expired.
|
|
6
|
+
*/
|
|
7
|
+
import { createClient } from '@supabase/supabase-js';
|
|
8
|
+
import { createDecipheriv, scryptSync } from 'crypto';
|
|
9
|
+
// Encryption configuration (must match lib/communications/channels/gmail.ts)
|
|
10
|
+
const ALGORITHM = 'aes-256-gcm';
|
|
11
|
+
// Google OAuth URLs
|
|
12
|
+
const GMAIL_TOKEN_URL = 'https://oauth2.googleapis.com/token';
|
|
13
|
+
/**
|
|
14
|
+
* Decrypt a token using AES-256-GCM
|
|
15
|
+
*/
|
|
16
|
+
function decryptToken(ciphertext, encryptionKey) {
|
|
17
|
+
const parts = ciphertext.split(':');
|
|
18
|
+
if (parts.length !== 4) {
|
|
19
|
+
throw new Error('Invalid encrypted token format');
|
|
20
|
+
}
|
|
21
|
+
const [saltHex, ivHex, authTagHex, encrypted] = parts;
|
|
22
|
+
const salt = Buffer.from(saltHex, 'hex');
|
|
23
|
+
const iv = Buffer.from(ivHex, 'hex');
|
|
24
|
+
const authTag = Buffer.from(authTagHex, 'hex');
|
|
25
|
+
const key = scryptSync(encryptionKey, salt, 32);
|
|
26
|
+
const decipher = createDecipheriv(ALGORITHM, key, iv);
|
|
27
|
+
decipher.setAuthTag(authTag);
|
|
28
|
+
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
|
|
29
|
+
decrypted += decipher.final('utf8');
|
|
30
|
+
return decrypted;
|
|
31
|
+
}
|
|
32
|
+
export class GmailTokenManager {
|
|
33
|
+
supabase;
|
|
34
|
+
clientId;
|
|
35
|
+
clientSecret;
|
|
36
|
+
encryptionKey;
|
|
37
|
+
constructor(config) {
|
|
38
|
+
this.supabase = createClient(config.supabaseUrl, config.supabaseServiceRoleKey);
|
|
39
|
+
this.clientId = config.clientId || process.env.GMAIL_CLIENT_ID || '';
|
|
40
|
+
this.clientSecret = config.clientSecret || process.env.GMAIL_CLIENT_SECRET || '';
|
|
41
|
+
this.encryptionKey = config.encryptionKey || process.env.TOKEN_ENCRYPTION_KEY || '';
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get a valid access token for the given email address.
|
|
45
|
+
* Automatically refreshes the token if expired.
|
|
46
|
+
*/
|
|
47
|
+
async getAccessToken(emailAddress) {
|
|
48
|
+
// Fetch stored token from database
|
|
49
|
+
const { data: tokenData, error } = await this.supabase
|
|
50
|
+
.from('email_oauth_tokens')
|
|
51
|
+
.select('*')
|
|
52
|
+
.eq('email_address', emailAddress)
|
|
53
|
+
.eq('is_active', true)
|
|
54
|
+
.single();
|
|
55
|
+
if (error || !tokenData) {
|
|
56
|
+
throw new Error(`No active OAuth token found for ${emailAddress}: ${error?.message || 'Not found'}`);
|
|
57
|
+
}
|
|
58
|
+
const row = tokenData;
|
|
59
|
+
// Check if token is expired (with 5 minute buffer)
|
|
60
|
+
const tokenExpiry = row.token_expiry
|
|
61
|
+
? new Date(row.token_expiry)
|
|
62
|
+
: new Date(0);
|
|
63
|
+
const needsRefresh = tokenExpiry.getTime() - Date.now() < 5 * 60 * 1000;
|
|
64
|
+
if (needsRefresh) {
|
|
65
|
+
console.log(`Token for ${emailAddress} is expired or expiring soon, refreshing...`);
|
|
66
|
+
return await this.refreshAndStoreToken(emailAddress, row.refresh_token_encrypted);
|
|
67
|
+
}
|
|
68
|
+
return row.access_token;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Refresh the access token using the refresh token
|
|
72
|
+
*/
|
|
73
|
+
async refreshAndStoreToken(emailAddress, encryptedRefreshToken) {
|
|
74
|
+
if (!this.clientId || !this.clientSecret || !this.encryptionKey) {
|
|
75
|
+
throw new Error('GMAIL_CLIENT_ID, GMAIL_CLIENT_SECRET, and TOKEN_ENCRYPTION_KEY must be configured');
|
|
76
|
+
}
|
|
77
|
+
// Decrypt refresh token
|
|
78
|
+
const refreshToken = decryptToken(encryptedRefreshToken, this.encryptionKey);
|
|
79
|
+
// Request new access token
|
|
80
|
+
const response = await fetch(GMAIL_TOKEN_URL, {
|
|
81
|
+
method: 'POST',
|
|
82
|
+
headers: {
|
|
83
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
84
|
+
},
|
|
85
|
+
body: new URLSearchParams({
|
|
86
|
+
client_id: this.clientId,
|
|
87
|
+
client_secret: this.clientSecret,
|
|
88
|
+
refresh_token: refreshToken,
|
|
89
|
+
grant_type: 'refresh_token',
|
|
90
|
+
}),
|
|
91
|
+
});
|
|
92
|
+
if (!response.ok) {
|
|
93
|
+
const error = await response.json();
|
|
94
|
+
throw new Error(`Token refresh failed: ${error.error_description || error.error}`);
|
|
95
|
+
}
|
|
96
|
+
const data = await response.json();
|
|
97
|
+
const newAccessToken = data.access_token;
|
|
98
|
+
const expiresAt = new Date(Date.now() + data.expires_in * 1000);
|
|
99
|
+
// Update in database
|
|
100
|
+
const { error: updateError } = await this.supabase
|
|
101
|
+
.from('email_oauth_tokens')
|
|
102
|
+
.update({
|
|
103
|
+
access_token: newAccessToken,
|
|
104
|
+
token_expiry: expiresAt.toISOString(),
|
|
105
|
+
updated_at: new Date().toISOString(),
|
|
106
|
+
})
|
|
107
|
+
.eq('email_address', emailAddress);
|
|
108
|
+
if (updateError) {
|
|
109
|
+
console.warn(`Failed to update token in database: ${updateError.message}`);
|
|
110
|
+
}
|
|
111
|
+
console.log(`Token refreshed for ${emailAddress}, expires at ${expiresAt.toISOString()}`);
|
|
112
|
+
return newAccessToken;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Check if a token exists for the given email address
|
|
116
|
+
*/
|
|
117
|
+
async hasToken(emailAddress) {
|
|
118
|
+
const { data, error } = await this.supabase
|
|
119
|
+
.from('email_oauth_tokens')
|
|
120
|
+
.select('email_address')
|
|
121
|
+
.eq('email_address', emailAddress)
|
|
122
|
+
.eq('is_active', true)
|
|
123
|
+
.single();
|
|
124
|
+
return !error && !!data;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Get a singleton token manager instance
|
|
129
|
+
*/
|
|
130
|
+
let tokenManagerInstance = null;
|
|
131
|
+
export function getGmailTokenManager(config) {
|
|
132
|
+
if (!tokenManagerInstance || config) {
|
|
133
|
+
const supabaseUrl = config?.supabaseUrl || process.env.SUPABASE_URL || process.env.NEXT_PUBLIC_SUPABASE_URL;
|
|
134
|
+
const supabaseServiceRoleKey = config?.supabaseServiceRoleKey || process.env.SUPABASE_SERVICE_ROLE_KEY;
|
|
135
|
+
if (!supabaseUrl || !supabaseServiceRoleKey) {
|
|
136
|
+
throw new Error('SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY are required');
|
|
137
|
+
}
|
|
138
|
+
tokenManagerInstance = new GmailTokenManager({
|
|
139
|
+
supabaseUrl,
|
|
140
|
+
supabaseServiceRoleKey,
|
|
141
|
+
...config,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
return tokenManagerInstance;
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=gmail-token-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gmail-token-manager.js","sourceRoot":"","sources":["../../src/extraction/gmail-token-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extraction Module Index
|
|
3
|
+
*
|
|
4
|
+
* Exports all extraction-related components for the Gmail Knowledge Base.
|
|
5
|
+
*/
|
|
6
|
+
export { GmailExtractor, type GmailThread, type GmailMessage, type ExtractedQAPair, type ExtractionResult, type GmailExtractorConfig, } from './gmail-extractor.js';
|
|
7
|
+
export { ExtractionPipeline, createExtractionPipeline, type PipelineConfig, type PipelineResult, } from './pipeline.js';
|
|
8
|
+
export { StateManager, getStateManager, resetStateManager, type ExtractionState, type BatchInfo, type StateManagerConfig, } from './state-manager.js';
|
|
9
|
+
export { RateLimiter, gmailRateLimiter, openaiRateLimiter, rateLimited, batchExecute, type RateLimiterConfig, type RateLimitStatus, } from './rate-limiter.js';
|
|
10
|
+
export { PIIHandler, getPIIHandler, resetPIIHandler, redactPII, detectPII, hasPII, type PIIType, type PIIMatch, type PIIResult, type PIIHandlerConfig, } from './pii-handler.js';
|
|
11
|
+
export { QualityFilter, getQualityFilter, resetQualityFilter, assessQuality, meetsMinimumQuality, inferResolutionIndicator, type QualityAssessment, type QualityFactor, type QualityFilterConfig, type QAInput, } from './quality-filter.js';
|
|
12
|
+
export { DeduplicationService, getDeduplicationService, resetDeduplicationService, hashContent, isDuplicate, type DeduplicationResult, type DeduplicationConfig, type QAContent, } from './deduplication.js';
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/extraction/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,cAAc,EACd,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,GAC1B,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACL,kBAAkB,EAClB,wBAAwB,EACxB,KAAK,cAAc,EACnB,KAAK,cAAc,GACpB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,YAAY,EACZ,eAAe,EACf,iBAAiB,EACjB,KAAK,eAAe,EACpB,KAAK,SAAS,EACd,KAAK,kBAAkB,GACxB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,EACX,YAAY,EACZ,KAAK,iBAAiB,EACtB,KAAK,eAAe,GACrB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,UAAU,EACV,aAAa,EACb,eAAe,EACf,SAAS,EACT,SAAS,EACT,MAAM,EACN,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,gBAAgB,GACtB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,aAAa,EACb,mBAAmB,EACnB,wBAAwB,EACxB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,OAAO,GACb,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,yBAAyB,EACzB,WAAW,EACX,WAAW,EACX,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,SAAS,GACf,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extraction Module Index
|
|
3
|
+
*
|
|
4
|
+
* Exports all extraction-related components for the Gmail Knowledge Base.
|
|
5
|
+
*/
|
|
6
|
+
// Core extraction
|
|
7
|
+
export { GmailExtractor, } from './gmail-extractor.js';
|
|
8
|
+
// Pipeline
|
|
9
|
+
export { ExtractionPipeline, createExtractionPipeline, } from './pipeline.js';
|
|
10
|
+
// State management
|
|
11
|
+
export { StateManager, getStateManager, resetStateManager, } from './state-manager.js';
|
|
12
|
+
// Rate limiting
|
|
13
|
+
export { RateLimiter, gmailRateLimiter, openaiRateLimiter, rateLimited, batchExecute, } from './rate-limiter.js';
|
|
14
|
+
// PII handling
|
|
15
|
+
export { PIIHandler, getPIIHandler, resetPIIHandler, redactPII, detectPII, hasPII, } from './pii-handler.js';
|
|
16
|
+
// Quality filtering
|
|
17
|
+
export { QualityFilter, getQualityFilter, resetQualityFilter, assessQuality, meetsMinimumQuality, inferResolutionIndicator, } from './quality-filter.js';
|
|
18
|
+
// Deduplication
|
|
19
|
+
export { DeduplicationService, getDeduplicationService, resetDeduplicationService, hashContent, isDuplicate, } from './deduplication.js';
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/extraction/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,kBAAkB;AAClB,OAAO,EACL,cAAc,GAMf,MAAM,sBAAsB,CAAC;AAE9B,WAAW;AACX,OAAO,EACL,kBAAkB,EAClB,wBAAwB,GAGzB,MAAM,eAAe,CAAC;AAEvB,mBAAmB;AACnB,OAAO,EACL,YAAY,EACZ,eAAe,EACf,iBAAiB,GAIlB,MAAM,oBAAoB,CAAC;AAE5B,gBAAgB;AAChB,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,EACX,YAAY,GAGb,MAAM,mBAAmB,CAAC;AAE3B,eAAe;AACf,OAAO,EACL,UAAU,EACV,aAAa,EACb,eAAe,EACf,SAAS,EACT,SAAS,EACT,MAAM,GAKP,MAAM,kBAAkB,CAAC;AAE1B,oBAAoB;AACpB,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,aAAa,EACb,mBAAmB,EACnB,wBAAwB,GAKzB,MAAM,qBAAqB,CAAC;AAE7B,gBAAgB;AAChB,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,yBAAyB,EACzB,WAAW,EACX,WAAW,GAIZ,MAAM,oBAAoB,CAAC"}
|