Governed substrate for autonomous agents: scoped identity (passports), audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
45 lines
3.7 KiB
JavaScript
45 lines
3.7 KiB
JavaScript
// ADR-160 end-to-end bridge proof against the REAL sysadmin@godcrm.ai mailbox.
|
|
// Replicates the /sync and /send handler logic (same imapFetch, same
|
|
// mail_messages upsert, same SMTPService transport). Run as a standalone
|
|
// process because the live godcrm predates these routes (stale, can't restart
|
|
// from inside it). This exercises the actual code paths, not mocks.
|
|
import 'dotenv/config';
|
|
import { dbGet, dbAll, dbRun } from '../../backend/database/connection.js';
|
|
import credentialVault from '../../backend/services/connectors/CredentialVault.js';
|
|
import { imapFetch } from '../../backend/services/mail/imapFetch.js';
|
|
import SMTPService from '../../backend/services/SMTPService.js';
|
|
|
|
const SPACE_ID = 11;
|
|
const row = await dbGet(`SELECT id, encrypted_payload FROM space_connectors WHERE space_id=? AND type_slug='imap' AND status='active' ORDER BY id DESC LIMIT 1`, [SPACE_ID]);
|
|
if (!row) { console.error('no imap connector'); process.exit(1); }
|
|
const creds = credentialVault.decrypt(typeof row.encrypted_payload === 'string' ? JSON.parse(row.encrypted_payload) : row.encrypted_payload);
|
|
console.log('connector id=%d user=%s', row.id, creds.username);
|
|
|
|
// ── SYNC (inbox) ──────────────────────────────────────────────
|
|
const fetched = await imapFetch({ creds, folder: 'inbox', limit: 50 });
|
|
console.log('IMAP fetched %d message(s) from inbox', fetched.length);
|
|
let synced = 0;
|
|
for (const m of fetched) {
|
|
await dbRun(
|
|
`INSERT INTO mail_messages (space_id, connector_id, folder, imap_uid, message_id, from_name, from_address, to_addresses, subject, preview, body_text, body_html, date, is_read)
|
|
VALUES (?, ?, 'inbox', ?, ?, ?, ?, ?::jsonb, ?, ?, ?, ?, ?, ?)
|
|
ON CONFLICT (connector_id, folder, imap_uid) DO UPDATE SET subject=EXCLUDED.subject, preview=EXCLUDED.preview, updated_at=now()`,
|
|
[SPACE_ID, row.id, m.imap_uid ?? null, m.message_id, m.from_name, m.from_address, JSON.stringify(m.to_addresses||[]), m.subject, m.preview, m.body_text, m.body_html, m.date, m.is_read]
|
|
);
|
|
synced++;
|
|
}
|
|
console.log('synced %d row(s) into mail_messages', synced);
|
|
|
|
// ── MESSAGES (read back as the UI would) ──────────────────────
|
|
const back = await dbAll(`SELECT id, folder, from_address, subject, preview, is_read, date FROM mail_messages WHERE space_id=? AND connector_id=? AND folder='inbox' ORDER BY date DESC NULLS LAST LIMIT 10`, [SPACE_ID, row.id]);
|
|
console.log('--- inbox rows ---');
|
|
for (const r of back) console.log(` [${r.is_read?'read':'NEW '}] ${r.from_address} | ${r.subject} | ${r.preview?.slice(0,50)||''}`);
|
|
|
|
// ── SEND (real SMTP submission) ───────────────────────────────
|
|
const transport = SMTPService.createTransport({ host: creds.smtp_host, port: Number(creds.smtp_port)||587, user: creds.username, password: creds.password });
|
|
const info = await transport.sendMail({ from: creds.username, to: creds.username, subject: 'ADR-160 round-trip send '+process.env.STAMP, text: 'sent via SMTPService.createTransport through the connector creds — bridge send path verified.' });
|
|
console.log('SENT ok messageId=%s', info.messageId);
|
|
await dbRun(`INSERT INTO mail_messages (space_id, connector_id, folder, message_id, from_name, from_address, to_addresses, subject, preview, body_text, body_html, date, is_read) VALUES (?, ?, 'sent', ?, ?, ?, ?::jsonb, ?, ?, ?, NULL, now(), true)`,
|
|
[SPACE_ID, row.id, info.messageId, creds.username, creds.username, JSON.stringify([creds.username]), 'ADR-160 round-trip send', 'bridge send path verified', 'bridge send path verified']);
|
|
console.log('Sent copy persisted. DONE.');
|
|
process.exit(0);
|