Kraft102's picture
Initial deployment - WidgeTDC Cortex Backend v2.1.0
529090e
import { DataSourceAdapter, type IngestedEntity } from '../ingestion/DataIngestionEngine.js';
import { readFile } from 'fs/promises';
import { existsSync } from 'fs';
interface OutlookEmail {
id: string;
subject: string;
sender: {
name: string;
address: string;
};
receivedDateTime: string;
bodyPreview: string;
body: {
contentType: string;
content: string;
};
importance: 'low' | 'normal' | 'high';
isRead: boolean;
}
export class OutlookJsonAdapter implements DataSourceAdapter {
name = 'Outlook';
type: 'outlook_mail' = 'outlook_mail';
private filePath: string;
private emails: OutlookEmail[] = [];
private lastLoaded: number = 0;
constructor(filePath: string) {
this.filePath = filePath;
}
/**
* Loads emails from the local JSON file
*/
private async loadEmails(): Promise<void> {
if (!existsSync(this.filePath)) {
console.warn(`[OutlookJsonAdapter] File not found: ${this.filePath}`);
this.emails = [];
return;
}
try {
const data = await readFile(this.filePath, 'utf-8');
// Handle potential wrapping structures (e.g. { value: [...] } or just [...])
const json = JSON.parse(data);
this.emails = Array.isArray(json) ? json : (json.value || []);
this.lastLoaded = Date.now();
console.log(`[OutlookJsonAdapter] Loaded ${this.emails.length} emails from ${this.filePath}`);
} catch (error) {
console.error(`[OutlookJsonAdapter] Failed to parse JSON:`, error);
this.emails = [];
}
}
/**
* Fetch emails, reloading if cache is stale (> 5 minutes)
*/
async fetch(): Promise<any[]> {
const STALE_THRESHOLD = 5 * 60 * 1000; // 5 minutes
if (this.emails.length === 0 || Date.now() - this.lastLoaded > STALE_THRESHOLD) {
await this.loadEmails();
}
return this.emails;
}
/**
* Transform Outlook emails to Unified Memory entities
*/
async transform(raw: any[]): Promise<IngestedEntity[]> {
return raw.map((email: OutlookEmail) => ({
id: email.id,
type: 'email',
source: 'Outlook',
title: email.subject,
content: `${email.subject}\n\nFrom: ${email.sender.name} <${email.sender.address}>\nDate: ${email.receivedDateTime}\n\n${email.bodyPreview}`,
metadata: {
subject: email.subject,
sender: email.sender.address,
senderName: email.sender.name,
receivedAt: email.receivedDateTime,
importance: email.importance,
isRead: email.isRead,
source: 'outlook_json',
},
timestamp: new Date(email.receivedDateTime)
}));
}
/**
* Check if the adapter is available (file exists)
*/
async isAvailable(): Promise<boolean> {
return existsSync(this.filePath);
}
/**
* Support direct queries
*/
async query(operation: string, params: any): Promise<any> {
await this.fetch(); // Ensure data is loaded
switch (operation) {
case 'search':
const term = params.query?.toLowerCase();
if (!term) return this.emails.slice(0, params.limit || 10);
return this.emails.filter(e =>
e.subject.toLowerCase().includes(term) ||
e.sender.name.toLowerCase().includes(term) ||
e.bodyPreview.toLowerCase().includes(term)
).slice(0, params.limit || 10);
case 'get_latest':
return this.emails
.sort((a, b) => new Date(b.receivedDateTime).getTime() - new Date(a.receivedDateTime).getTime())
.slice(0, params.limit || 10);
case 'get_by_sender':
const sender = params.sender?.toLowerCase();
if (!sender) return [];
return this.emails.filter(e =>
e.sender.address.toLowerCase().includes(sender) ||
e.sender.name.toLowerCase().includes(sender)
).slice(0, params.limit || 10);
default:
throw new Error(`Unsupported operation: ${operation}`);
}
}
}