@datasynx/agentic-crm 1.6.0 → 1.8.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.
Files changed (49) hide show
  1. package/dist/cli.js +39 -70
  2. package/dist/cli.js.map +1 -1
  3. package/dist/daemon/worker.js +24 -1
  4. package/dist/daemon/worker.js.map +1 -1
  5. package/dist/{imap-o6PRuBvm.js → imap-BRgNh3T3.js} +2 -2
  6. package/dist/{imap-o6PRuBvm.js.map → imap-BRgNh3T3.js.map} +1 -1
  7. package/dist/imap-DzeqMdZ3.js +2 -0
  8. package/dist/{index-BBAlKZg6.d.ts → index-DoYT-azq.d.ts} +8 -8
  9. package/dist/index-DoYT-azq.d.ts.map +1 -0
  10. package/dist/{index-DNHsURo5.d.cts → index-Ewy4f1XW.d.cts} +12 -12
  11. package/dist/{index-DNHsURo5.d.cts.map → index-Ewy4f1XW.d.cts.map} +1 -1
  12. package/dist/index.d.cts +12 -12
  13. package/dist/index.d.cts.map +1 -1
  14. package/dist/index.d.ts +8 -8
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/{knowledge-base-Cc0niBFf.js → knowledge-base-Byo0zwM5.js} +2 -1
  17. package/dist/knowledge-base-Byo0zwM5.js.map +1 -0
  18. package/dist/{login-CYgla6-A.js → login-yt9OOQQk.js} +3 -2
  19. package/dist/{login-CYgla6-A.js.map → login-yt9OOQQk.js.map} +1 -1
  20. package/dist/mailbox-config-Bu-J1O4I.js +2 -0
  21. package/dist/mailbox-config-Dn2xTn9N.js +67 -0
  22. package/dist/mailbox-config-Dn2xTn9N.js.map +1 -0
  23. package/dist/mailbox-poll-B8dvFAXT.js +80 -0
  24. package/dist/mailbox-poll-B8dvFAXT.js.map +1 -0
  25. package/dist/mcp-CdTJWTJf.d.cts.map +1 -1
  26. package/dist/mcp-CdTJWTJf.d.ts.map +1 -1
  27. package/dist/mcp.cjs +303 -137
  28. package/dist/mcp.cjs.map +1 -1
  29. package/dist/mcp.d.cts.map +1 -1
  30. package/dist/mcp.d.ts.map +1 -1
  31. package/dist/mcp.js +303 -137
  32. package/dist/mcp.js.map +1 -1
  33. package/dist/{microsoft-DgbVlHdT.js → microsoft-dsC1fQQG.js} +2 -38
  34. package/dist/microsoft-dsC1fQQG.js.map +1 -0
  35. package/dist/{server-BbInMUgp.js → server-C0XkJQBo.js} +167 -132
  36. package/dist/server-C0XkJQBo.js.map +1 -0
  37. package/dist/{token-resolver-BRLOmRvF.js → token-resolver-D98qPOOf.js} +3 -2
  38. package/dist/{token-resolver-BRLOmRvF.js.map → token-resolver-D98qPOOf.js.map} +1 -1
  39. package/dist/token-store-B0h0USqe.js +43 -0
  40. package/dist/token-store-B0h0USqe.js.map +1 -0
  41. package/dist/token-store-CEmz8d-0.js +2 -0
  42. package/dist/velocity-BtX1l5Gh.js +2 -0
  43. package/dist/velocity-C2l4cW0K.js +123 -0
  44. package/dist/velocity-C2l4cW0K.js.map +1 -0
  45. package/package.json +1 -1
  46. package/dist/index-BBAlKZg6.d.ts.map +0 -1
  47. package/dist/knowledge-base-Cc0niBFf.js.map +0 -1
  48. package/dist/microsoft-DgbVlHdT.js.map +0 -1
  49. package/dist/server-BbInMUgp.js.map +0 -1
@@ -173,8 +173,31 @@ async function takeDailySnapshot() {
173
173
  logger.error("daemon", "snapshot failed", { error: err.message });
174
174
  }
175
175
  }
176
- new CronJob(`*/${Math.max(1, parseInt(process.env["DXCRM_DAEMON_INTERVAL"] ?? "30", 10) || 30)} * * * *`, async () => {
176
+ /**
177
+ * Poll every configured mailbox (stored OAuth accounts + env IMAP) and
178
+ * auto-route new mail to customers by domain. The window overlaps the interval
179
+ * so nothing is missed; dedup keeps it idempotent.
180
+ */
181
+ async function pollMailboxes(intervalMin) {
182
+ try {
183
+ const { listMailboxTokens } = await import("../token-store-CEmz8d-0.js");
184
+ const { imapConfigFromEnv } = await import("../mailbox-config-Bu-J1O4I.js");
185
+ if (listMailboxTokens(DATA_DIR).length === 0 && imapConfigFromEnv() === null) return;
186
+ const { runMailboxPollCycle } = await import("../mailbox-poll-B8dvFAXT.js");
187
+ const result = await runMailboxPollCycle(DATA_DIR, /* @__PURE__ */ new Date(Date.now() - (intervalMin + 5) * 60 * 1e3));
188
+ if (result.synced > 0) logger.info("daemon", "mailbox cycle", {
189
+ accounts: result.accounts,
190
+ synced: result.synced,
191
+ unrouted: result.unrouted
192
+ });
193
+ } catch (err) {
194
+ logger.error("daemon", "mailbox poll cycle failed", { error: err.message });
195
+ }
196
+ }
197
+ const daemonIntervalMin = Math.max(1, parseInt(process.env["DXCRM_DAEMON_INTERVAL"] ?? "30", 10) || 30);
198
+ new CronJob(`*/${daemonIntervalMin} * * * *`, async () => {
177
199
  await syncAllCustomers();
200
+ await pollMailboxes(daemonIntervalMin);
178
201
  await checkAgentWakeTriggers().catch((err) => {
179
202
  logger.error("daemon", "wake trigger check failed", { error: err.message });
180
203
  });
@@ -1 +1 @@
1
- {"version":3,"file":"worker.js","names":[],"sources":["../../src/daemon/worker.ts"],"sourcesContent":["// src/daemon/worker.ts\n// Standalone detached process — started by `dxcrm daemon start`\n// Handles background Gmail sync + transcript watching via cron\nimport { CronJob } from \"cron\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { logger } from \"../core/logger.js\";\nimport { writeJsonFile } from \"../fs/json-store.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nconst MAX_CUSTOMERS_PER_CYCLE = 50;\n\nasync function syncWithBackoff(fn: () => Promise<void>, maxRetries = 3): Promise<void> {\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n await fn();\n return;\n } catch (err) {\n const msg = (err as Error).message;\n if (msg.includes(\"429\") || msg.includes(\"rateLimitExceeded\")) {\n const delay = Math.pow(2, attempt) * 2000; // 2s, 4s, 8s\n logger.warn(\"daemon\", \"rate limit, retrying\", { delayMs: delay });\n await new Promise((r) => setTimeout(r, delay));\n } else {\n throw err;\n }\n }\n }\n}\n\nasync function syncAllCustomers(): Promise<void> {\n const customersDir = path.join(DATA_DIR, \"customers\");\n if (!fs.existsSync(customersDir)) return;\n\n const slugs = fs.readdirSync(customersDir).filter((s) => {\n try {\n return fs.statSync(path.join(customersDir, s)).isDirectory();\n } catch {\n return false;\n }\n });\n\n const slugsToSync = slugs.slice(0, MAX_CUSTOMERS_PER_CYCLE);\n\n for (const slug of slugsToSync) {\n const sourcesPath = path.join(customersDir, slug, \"sources.json\");\n if (!fs.existsSync(sourcesPath)) continue;\n\n try {\n const sources = JSON.parse(fs.readFileSync(sourcesPath, \"utf-8\")) as {\n gmail?: { query?: string; enabled?: boolean };\n };\n\n if (sources.gmail?.enabled && sources.gmail.query) {\n // Gmail sync requires auth — skip if token not configured\n const tokenPath = path.join(DATA_DIR, \".agentic\", \"gmail-token.json\");\n const credPath = path.join(DATA_DIR, \".agentic\", \"gmail-credentials.json\");\n if (fs.existsSync(tokenPath) && fs.existsSync(credPath)) {\n const { getGmailAuth } = await import(\"../sync/gmail-auth.js\");\n const { syncGmail } = await import(\"../sync/gmail-sync.js\");\n const auth = await getGmailAuth(credPath, tokenPath);\n await syncWithBackoff(async () => {\n const result = await syncGmail({\n slug,\n dataDir: DATA_DIR,\n auth,\n query: sources.gmail!.query!,\n since: new Date(Date.now() - 30 * 60 * 1000), // last 30 min\n });\n if (result.synced > 0) {\n logger.info(\"daemon\", \"synced emails\", { slug, synced: result.synced });\n }\n // Update sync state after each successful customer sync\n const { updateSlugSyncState } = await import(\"../fs/sync-state.js\");\n updateSlugSyncState(DATA_DIR, slug, { lastGmailSync: new Date().toISOString() });\n });\n }\n }\n } catch (err) {\n logger.error(\"daemon\", \"error syncing customer\", { slug, error: (err as Error).message });\n }\n }\n}\n\n// Start transcript watcher\nasync function startWatcher(): Promise<void> {\n const agenticSourcesPath = path.join(DATA_DIR, \".agentic\", \"sources.json\");\n if (!fs.existsSync(agenticSourcesPath)) return;\n\n try {\n const sources = JSON.parse(fs.readFileSync(agenticSourcesPath, \"utf-8\")) as {\n transcripts?: { paths?: string[]; extensions?: string[]; enabled?: boolean };\n };\n\n if (sources.transcripts?.enabled && sources.transcripts.paths?.length) {\n const { watchTranscripts, processTranscriptFileAutoMatch } =\n await import(\"../sync/transcript-watcher.js\");\n watchTranscripts({\n paths: sources.transcripts.paths,\n extensions: sources.transcripts.extensions ?? [\".txt\", \".vtt\"],\n dataDir: DATA_DIR,\n onFile: (filePath) => processTranscriptFileAutoMatch(filePath, DATA_DIR),\n });\n logger.info(\"daemon\", \"watching transcripts (LLM auto-match)\");\n }\n } catch (err) {\n logger.error(\"daemon\", \"watcher error\", { error: (err as Error).message });\n }\n}\n\nasync function checkAgentWakeTriggers(): Promise<void> {\n const agentsDir = path.join(DATA_DIR, \".agentic\", \"agents\");\n if (!fs.existsSync(agentsDir)) return;\n\n const files = fs.readdirSync(agentsDir).filter((f) => f.endsWith(\".agent.json\"));\n\n for (const file of files) {\n try {\n const config = JSON.parse(fs.readFileSync(path.join(agentsDir, file), \"utf-8\") as string) as {\n slug: string;\n channel: string;\n wakeOn: string[];\n lastWake: string | null;\n telegramChatId?: string;\n };\n\n if (!config.wakeOn.includes(\"email\")) continue;\n\n const { getLastGmailSync } = await import(\"../fs/sync-state.js\");\n const lastSync = getLastGmailSync(DATA_DIR, config.slug);\n const lastWake = config.lastWake ? new Date(config.lastWake) : null;\n\n if (!lastSync) continue;\n if (lastWake && lastSync <= lastWake) continue;\n\n // New email since last wake — build context and send notification\n logger.info(\"daemon\", \"wake trigger\", { slug: config.slug });\n\n const { buildContext } = await import(\"../core/context-builder.js\");\n const context = await buildContext(DATA_DIR, config.slug).catch(() => null);\n if (!context) continue;\n\n if (\n config.channel === \"telegram\" &&\n process.env[\"TELEGRAM_BOT_TOKEN\"] &&\n (config.telegramChatId ?? process.env[\"TELEGRAM_CHAT_ID\"])\n ) {\n const chatId = config.telegramChatId ?? process.env[\"TELEGRAM_CHAT_ID\"]!;\n const token = process.env[\"TELEGRAM_BOT_TOKEN\"];\n const message = `📬 New activity: *${config.slug}*\\n\\n${context.slice(0, 800)}`;\n\n try {\n const { default: https } = await import(\"https\");\n const body = JSON.stringify({ chat_id: chatId, text: message, parse_mode: \"Markdown\" });\n await new Promise<void>((resolve, reject) => {\n const req = https.request(\n `https://api.telegram.org/bot${token}/sendMessage`,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Content-Length\": Buffer.byteLength(body),\n },\n },\n (res) => {\n res.resume();\n resolve();\n }\n );\n req.on(\"error\", reject);\n req.write(body);\n req.end();\n });\n logger.info(\"daemon\", \"telegram sent\", { slug: config.slug });\n } catch (err) {\n logger.error(\"daemon\", \"telegram failed\", { error: (err as Error).message });\n }\n }\n\n // Update lastWake\n config.lastWake = new Date().toISOString();\n writeJsonFile(path.join(agentsDir, file), config);\n } catch (err) {\n logger.error(\"daemon\", \"agent check error\", { file, error: (err as Error).message });\n }\n }\n}\n\n/**\n * Self-healing: each cycle, clean orphaned atomic-write temp files (a crash\n * signature) and log any *failed* health checks (e.g. invalid customer data).\n * Warn-level checks (log errors, stale backups) are intentionally not re-logged\n * here to avoid a feedback loop — `dxcrm doctor` surfaces those on demand.\n */\nasync function runSelfHeal(): Promise<void> {\n try {\n const { runDiagnostics, cleanupTempFiles } = await import(\"../core/doctor.js\");\n const removed = cleanupTempFiles(DATA_DIR);\n if (removed.length > 0) {\n logger.warn(\"daemon\", \"self-heal: removed orphaned temp files\", { count: removed.length });\n }\n const report = await runDiagnostics(DATA_DIR);\n for (const check of report.checks) {\n if (check.status === \"fail\") {\n logger.error(\"daemon\", `self-check failed: ${check.name}`, { detail: check.detail });\n }\n }\n } catch (err) {\n logger.error(\"daemon\", \"self-heal failed\", { error: (err as Error).message });\n }\n}\n\n/** Take a pipeline snapshot once per day (self-populating time-travel history). */\nasync function takeDailySnapshot(): Promise<void> {\n try {\n const { listSnapshots, takeSnapshot } = await import(\"../core/snapshots.js\");\n const today = new Date().toISOString().slice(0, 10);\n if (listSnapshots(DATA_DIR).some((s) => s.id === today)) return; // already taken today\n const snap = takeSnapshot(DATA_DIR, today);\n logger.info(\"daemon\", \"pipeline snapshot taken\", { id: snap.id, deals: snap.deals.length });\n } catch (err) {\n logger.error(\"daemon\", \"snapshot failed\", { error: (err as Error).message });\n }\n}\n\n// Gmail sync — interval configurable via DXCRM_DAEMON_INTERVAL (minutes, default 30)\nconst daemonIntervalMin = Math.max(\n 1,\n parseInt(process.env[\"DXCRM_DAEMON_INTERVAL\"] ?? \"30\", 10) || 30\n);\nnew CronJob(\n `*/${daemonIntervalMin} * * * *`,\n async () => {\n await syncAllCustomers();\n await checkAgentWakeTriggers().catch((err: unknown) => {\n logger.error(\"daemon\", \"wake trigger check failed\", { error: (err as Error).message });\n });\n await runSelfHeal();\n await takeDailySnapshot();\n },\n null,\n true,\n undefined,\n null,\n false,\n undefined,\n false, // unrefTimeout — keep event loop alive\n true // waitForCompletion\n);\n\n// Scheduled backup check — hourly, runs backup if >1 day since last\nnew CronJob(\n \"*/60 * * * *\",\n async () => {\n try {\n const { runScheduledBackupIfDue } = await import(\"../commands/backup.js\");\n await runScheduledBackupIfDue(DATA_DIR);\n } catch (err) {\n logger.error(\"daemon\", \"backup check error\", { error: (err as Error).message });\n }\n },\n null,\n true,\n undefined,\n null,\n false,\n undefined,\n false, // unrefTimeout — keep event loop alive\n true // waitForCompletion\n);\n\n// Daily push subscription renewal at 06:00\nnew CronJob(\n \"0 6 * * *\",\n async () => {\n try {\n const { renewExpiringSubscriptions } = await import(\"../sync/push-manager.js\");\n const { buildGmailRenewFn } = await import(\"../sync/gmail-webhook-handler.js\");\n const tokenPath = path.join(DATA_DIR, \".agentic\", \"gmail-token.json\");\n const credPath = path.join(DATA_DIR, \".agentic\", \"gmail-credentials.json\");\n const { readSubscriptions } = await import(\"../sync/push-manager.js\");\n const subs = await readSubscriptions(DATA_DIR);\n const gmailSubs = subs.filter((s) => s.provider === \"gmail\" && s.status === \"active\");\n if (gmailSubs.length === 0) return;\n if (!fs.existsSync(tokenPath) || !fs.existsSync(credPath)) return;\n const { getGmailAuth } = await import(\"../sync/gmail-auth.js\");\n const auth = await getGmailAuth(credPath, tokenPath);\n const token = (auth.credentials?.access_token as string | undefined) ?? \"\";\n const result = await renewExpiringSubscriptions(DATA_DIR, buildGmailRenewFn(token, \"\"), 24);\n if (result.renewed.length > 0) {\n logger.info(\"push\", \"renewed subscriptions\", { count: result.renewed.length });\n }\n if (result.errors.length > 0) {\n logger.warn(\"push\", \"renewal errors\", { errors: result.errors });\n }\n } catch (err) {\n logger.error(\"push\", \"renewal failed\", { error: (err as Error).message });\n }\n },\n null,\n true,\n undefined,\n null,\n false,\n undefined,\n false, // unrefTimeout — keep event loop alive\n true // waitForCompletion\n);\n\n// Daily proactive checks at 07:00 — relationship decay, deal risk, daily briefing\nnew CronJob(\n \"0 7 * * *\",\n async () => {\n try {\n const { runDailyProactiveChecks } = await import(\"../daemon/proactive-worker.js\");\n const result = await runDailyProactiveChecks(DATA_DIR);\n logger.info(\"proactive\", \"daily check\", {\n customersChecked: result.customersChecked,\n tasksEnqueued: result.tasksEnqueued,\n });\n if (result.errors.length > 0) {\n logger.warn(\"proactive\", \"errors during daily check\", { errors: result.errors });\n }\n const { drainProactiveQueue } = await import(\"../core/notification-dispatcher.js\");\n const drain = await drainProactiveQueue(DATA_DIR);\n logger.info(\"proactive\", \"dispatched tasks\", { sent: drain.sent, failed: drain.failed });\n const { syncGoalProgressFromPipeline } = await import(\"../core/goal-engine.js\");\n const goalSync = await syncGoalProgressFromPipeline(DATA_DIR);\n if (goalSync.updated.length > 0) {\n logger.info(\"goals\", \"progress synced\", { updated: goalSync.updated });\n }\n } catch (err) {\n logger.error(\"proactive\", \"daily check failed\", { error: (err as Error).message });\n }\n },\n null,\n true,\n undefined,\n null,\n false,\n undefined,\n false, // unrefTimeout — keep event loop alive\n true // waitForCompletion\n);\n\n// SLA breach check — daily at 08:00\nnew CronJob(\n \"0 8 * * *\",\n async () => {\n try {\n const { checkSlaBreaches } = await import(\"../core/sla-engine.js\");\n const today = new Date().toISOString().slice(0, 10);\n const breaches = await checkSlaBreaches(DATA_DIR, today);\n if (breaches.length > 0) {\n logger.warn(\"tickets\", \"SLA breaches found\", { count: breaches.length });\n for (const { slug, ticket } of breaches) {\n logger.warn(\"tickets\", \"SLA breach\", {\n slug,\n ticketId: ticket.id,\n title: ticket.title,\n due: ticket.slaDue,\n });\n }\n }\n } catch (err) {\n logger.error(\"tickets\", \"SLA check failed\", { error: (err as Error).message });\n }\n },\n null,\n true,\n undefined,\n null,\n false,\n undefined,\n false, // unrefTimeout — keep event loop alive\n true // waitForCompletion\n);\n\n// Email sequence cycle — every 6 hours\nnew CronJob(\n \"0 */6 * * *\",\n async () => {\n try {\n const { runSequenceCycle } = await import(\"../core/sequence-engine.js\");\n const today = new Date().toISOString().slice(0, 10);\n const result = await runSequenceCycle(DATA_DIR, today);\n logger.info(\"sequences\", \"cycle complete\", {\n sent: result.sent,\n completed: result.completed,\n errors: result.errors.length,\n });\n } catch (err) {\n logger.error(\"sequences\", \"cycle failed\", { error: (err as Error).message });\n }\n },\n null,\n true,\n undefined,\n null,\n false,\n undefined,\n false, // unrefTimeout — keep event loop alive\n true // waitForCompletion\n);\n\nawait startWatcher();\n\n// Signal ready\nif (process.send) process.send(\"ready\");\nlogger.info(\"daemon\", \"daemon started\");\n"],"mappings":"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"}
1
+ {"version":3,"file":"worker.js","names":[],"sources":["../../src/daemon/worker.ts"],"sourcesContent":["// src/daemon/worker.ts\n// Standalone detached process — started by `dxcrm daemon start`\n// Handles background Gmail sync + transcript watching via cron\nimport { CronJob } from \"cron\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { logger } from \"../core/logger.js\";\nimport { writeJsonFile } from \"../fs/json-store.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nconst MAX_CUSTOMERS_PER_CYCLE = 50;\n\nasync function syncWithBackoff(fn: () => Promise<void>, maxRetries = 3): Promise<void> {\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n await fn();\n return;\n } catch (err) {\n const msg = (err as Error).message;\n if (msg.includes(\"429\") || msg.includes(\"rateLimitExceeded\")) {\n const delay = Math.pow(2, attempt) * 2000; // 2s, 4s, 8s\n logger.warn(\"daemon\", \"rate limit, retrying\", { delayMs: delay });\n await new Promise((r) => setTimeout(r, delay));\n } else {\n throw err;\n }\n }\n }\n}\n\nasync function syncAllCustomers(): Promise<void> {\n const customersDir = path.join(DATA_DIR, \"customers\");\n if (!fs.existsSync(customersDir)) return;\n\n const slugs = fs.readdirSync(customersDir).filter((s) => {\n try {\n return fs.statSync(path.join(customersDir, s)).isDirectory();\n } catch {\n return false;\n }\n });\n\n const slugsToSync = slugs.slice(0, MAX_CUSTOMERS_PER_CYCLE);\n\n for (const slug of slugsToSync) {\n const sourcesPath = path.join(customersDir, slug, \"sources.json\");\n if (!fs.existsSync(sourcesPath)) continue;\n\n try {\n const sources = JSON.parse(fs.readFileSync(sourcesPath, \"utf-8\")) as {\n gmail?: { query?: string; enabled?: boolean };\n };\n\n if (sources.gmail?.enabled && sources.gmail.query) {\n // Gmail sync requires auth — skip if token not configured\n const tokenPath = path.join(DATA_DIR, \".agentic\", \"gmail-token.json\");\n const credPath = path.join(DATA_DIR, \".agentic\", \"gmail-credentials.json\");\n if (fs.existsSync(tokenPath) && fs.existsSync(credPath)) {\n const { getGmailAuth } = await import(\"../sync/gmail-auth.js\");\n const { syncGmail } = await import(\"../sync/gmail-sync.js\");\n const auth = await getGmailAuth(credPath, tokenPath);\n await syncWithBackoff(async () => {\n const result = await syncGmail({\n slug,\n dataDir: DATA_DIR,\n auth,\n query: sources.gmail!.query!,\n since: new Date(Date.now() - 30 * 60 * 1000), // last 30 min\n });\n if (result.synced > 0) {\n logger.info(\"daemon\", \"synced emails\", { slug, synced: result.synced });\n }\n // Update sync state after each successful customer sync\n const { updateSlugSyncState } = await import(\"../fs/sync-state.js\");\n updateSlugSyncState(DATA_DIR, slug, { lastGmailSync: new Date().toISOString() });\n });\n }\n }\n } catch (err) {\n logger.error(\"daemon\", \"error syncing customer\", { slug, error: (err as Error).message });\n }\n }\n}\n\n// Start transcript watcher\nasync function startWatcher(): Promise<void> {\n const agenticSourcesPath = path.join(DATA_DIR, \".agentic\", \"sources.json\");\n if (!fs.existsSync(agenticSourcesPath)) return;\n\n try {\n const sources = JSON.parse(fs.readFileSync(agenticSourcesPath, \"utf-8\")) as {\n transcripts?: { paths?: string[]; extensions?: string[]; enabled?: boolean };\n };\n\n if (sources.transcripts?.enabled && sources.transcripts.paths?.length) {\n const { watchTranscripts, processTranscriptFileAutoMatch } =\n await import(\"../sync/transcript-watcher.js\");\n watchTranscripts({\n paths: sources.transcripts.paths,\n extensions: sources.transcripts.extensions ?? [\".txt\", \".vtt\"],\n dataDir: DATA_DIR,\n onFile: (filePath) => processTranscriptFileAutoMatch(filePath, DATA_DIR),\n });\n logger.info(\"daemon\", \"watching transcripts (LLM auto-match)\");\n }\n } catch (err) {\n logger.error(\"daemon\", \"watcher error\", { error: (err as Error).message });\n }\n}\n\nasync function checkAgentWakeTriggers(): Promise<void> {\n const agentsDir = path.join(DATA_DIR, \".agentic\", \"agents\");\n if (!fs.existsSync(agentsDir)) return;\n\n const files = fs.readdirSync(agentsDir).filter((f) => f.endsWith(\".agent.json\"));\n\n for (const file of files) {\n try {\n const config = JSON.parse(fs.readFileSync(path.join(agentsDir, file), \"utf-8\") as string) as {\n slug: string;\n channel: string;\n wakeOn: string[];\n lastWake: string | null;\n telegramChatId?: string;\n };\n\n if (!config.wakeOn.includes(\"email\")) continue;\n\n const { getLastGmailSync } = await import(\"../fs/sync-state.js\");\n const lastSync = getLastGmailSync(DATA_DIR, config.slug);\n const lastWake = config.lastWake ? new Date(config.lastWake) : null;\n\n if (!lastSync) continue;\n if (lastWake && lastSync <= lastWake) continue;\n\n // New email since last wake — build context and send notification\n logger.info(\"daemon\", \"wake trigger\", { slug: config.slug });\n\n const { buildContext } = await import(\"../core/context-builder.js\");\n const context = await buildContext(DATA_DIR, config.slug).catch(() => null);\n if (!context) continue;\n\n if (\n config.channel === \"telegram\" &&\n process.env[\"TELEGRAM_BOT_TOKEN\"] &&\n (config.telegramChatId ?? process.env[\"TELEGRAM_CHAT_ID\"])\n ) {\n const chatId = config.telegramChatId ?? process.env[\"TELEGRAM_CHAT_ID\"]!;\n const token = process.env[\"TELEGRAM_BOT_TOKEN\"];\n const message = `📬 New activity: *${config.slug}*\\n\\n${context.slice(0, 800)}`;\n\n try {\n const { default: https } = await import(\"https\");\n const body = JSON.stringify({ chat_id: chatId, text: message, parse_mode: \"Markdown\" });\n await new Promise<void>((resolve, reject) => {\n const req = https.request(\n `https://api.telegram.org/bot${token}/sendMessage`,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Content-Length\": Buffer.byteLength(body),\n },\n },\n (res) => {\n res.resume();\n resolve();\n }\n );\n req.on(\"error\", reject);\n req.write(body);\n req.end();\n });\n logger.info(\"daemon\", \"telegram sent\", { slug: config.slug });\n } catch (err) {\n logger.error(\"daemon\", \"telegram failed\", { error: (err as Error).message });\n }\n }\n\n // Update lastWake\n config.lastWake = new Date().toISOString();\n writeJsonFile(path.join(agentsDir, file), config);\n } catch (err) {\n logger.error(\"daemon\", \"agent check error\", { file, error: (err as Error).message });\n }\n }\n}\n\n/**\n * Self-healing: each cycle, clean orphaned atomic-write temp files (a crash\n * signature) and log any *failed* health checks (e.g. invalid customer data).\n * Warn-level checks (log errors, stale backups) are intentionally not re-logged\n * here to avoid a feedback loop — `dxcrm doctor` surfaces those on demand.\n */\nasync function runSelfHeal(): Promise<void> {\n try {\n const { runDiagnostics, cleanupTempFiles } = await import(\"../core/doctor.js\");\n const removed = cleanupTempFiles(DATA_DIR);\n if (removed.length > 0) {\n logger.warn(\"daemon\", \"self-heal: removed orphaned temp files\", { count: removed.length });\n }\n const report = await runDiagnostics(DATA_DIR);\n for (const check of report.checks) {\n if (check.status === \"fail\") {\n logger.error(\"daemon\", `self-check failed: ${check.name}`, { detail: check.detail });\n }\n }\n } catch (err) {\n logger.error(\"daemon\", \"self-heal failed\", { error: (err as Error).message });\n }\n}\n\n/** Take a pipeline snapshot once per day (self-populating time-travel history). */\nasync function takeDailySnapshot(): Promise<void> {\n try {\n const { listSnapshots, takeSnapshot } = await import(\"../core/snapshots.js\");\n const today = new Date().toISOString().slice(0, 10);\n if (listSnapshots(DATA_DIR).some((s) => s.id === today)) return; // already taken today\n const snap = takeSnapshot(DATA_DIR, today);\n logger.info(\"daemon\", \"pipeline snapshot taken\", { id: snap.id, deals: snap.deals.length });\n } catch (err) {\n logger.error(\"daemon\", \"snapshot failed\", { error: (err as Error).message });\n }\n}\n\n/**\n * Poll every configured mailbox (stored OAuth accounts + env IMAP) and\n * auto-route new mail to customers by domain. The window overlaps the interval\n * so nothing is missed; dedup keeps it idempotent.\n */\nasync function pollMailboxes(intervalMin: number): Promise<void> {\n try {\n const { listMailboxTokens } = await import(\"../sync/oauth/token-store.js\");\n const { imapConfigFromEnv } = await import(\"../sync/mailbox-config.js\");\n if (listMailboxTokens(DATA_DIR).length === 0 && imapConfigFromEnv() === null) return;\n\n const { runMailboxPollCycle } = await import(\"./mailbox-poll.js\");\n const since = new Date(Date.now() - (intervalMin + 5) * 60 * 1000);\n const result = await runMailboxPollCycle(DATA_DIR, since);\n if (result.synced > 0) {\n logger.info(\"daemon\", \"mailbox cycle\", {\n accounts: result.accounts,\n synced: result.synced,\n unrouted: result.unrouted,\n });\n }\n } catch (err) {\n logger.error(\"daemon\", \"mailbox poll cycle failed\", { error: (err as Error).message });\n }\n}\n\n// Gmail sync — interval configurable via DXCRM_DAEMON_INTERVAL (minutes, default 30)\nconst daemonIntervalMin = Math.max(\n 1,\n parseInt(process.env[\"DXCRM_DAEMON_INTERVAL\"] ?? \"30\", 10) || 30\n);\nnew CronJob(\n `*/${daemonIntervalMin} * * * *`,\n async () => {\n await syncAllCustomers();\n await pollMailboxes(daemonIntervalMin);\n await checkAgentWakeTriggers().catch((err: unknown) => {\n logger.error(\"daemon\", \"wake trigger check failed\", { error: (err as Error).message });\n });\n await runSelfHeal();\n await takeDailySnapshot();\n },\n null,\n true,\n undefined,\n null,\n false,\n undefined,\n false, // unrefTimeout — keep event loop alive\n true // waitForCompletion\n);\n\n// Scheduled backup check — hourly, runs backup if >1 day since last\nnew CronJob(\n \"*/60 * * * *\",\n async () => {\n try {\n const { runScheduledBackupIfDue } = await import(\"../commands/backup.js\");\n await runScheduledBackupIfDue(DATA_DIR);\n } catch (err) {\n logger.error(\"daemon\", \"backup check error\", { error: (err as Error).message });\n }\n },\n null,\n true,\n undefined,\n null,\n false,\n undefined,\n false, // unrefTimeout — keep event loop alive\n true // waitForCompletion\n);\n\n// Daily push subscription renewal at 06:00\nnew CronJob(\n \"0 6 * * *\",\n async () => {\n try {\n const { renewExpiringSubscriptions } = await import(\"../sync/push-manager.js\");\n const { buildGmailRenewFn } = await import(\"../sync/gmail-webhook-handler.js\");\n const tokenPath = path.join(DATA_DIR, \".agentic\", \"gmail-token.json\");\n const credPath = path.join(DATA_DIR, \".agentic\", \"gmail-credentials.json\");\n const { readSubscriptions } = await import(\"../sync/push-manager.js\");\n const subs = await readSubscriptions(DATA_DIR);\n const gmailSubs = subs.filter((s) => s.provider === \"gmail\" && s.status === \"active\");\n if (gmailSubs.length === 0) return;\n if (!fs.existsSync(tokenPath) || !fs.existsSync(credPath)) return;\n const { getGmailAuth } = await import(\"../sync/gmail-auth.js\");\n const auth = await getGmailAuth(credPath, tokenPath);\n const token = (auth.credentials?.access_token as string | undefined) ?? \"\";\n const result = await renewExpiringSubscriptions(DATA_DIR, buildGmailRenewFn(token, \"\"), 24);\n if (result.renewed.length > 0) {\n logger.info(\"push\", \"renewed subscriptions\", { count: result.renewed.length });\n }\n if (result.errors.length > 0) {\n logger.warn(\"push\", \"renewal errors\", { errors: result.errors });\n }\n } catch (err) {\n logger.error(\"push\", \"renewal failed\", { error: (err as Error).message });\n }\n },\n null,\n true,\n undefined,\n null,\n false,\n undefined,\n false, // unrefTimeout — keep event loop alive\n true // waitForCompletion\n);\n\n// Daily proactive checks at 07:00 — relationship decay, deal risk, daily briefing\nnew CronJob(\n \"0 7 * * *\",\n async () => {\n try {\n const { runDailyProactiveChecks } = await import(\"../daemon/proactive-worker.js\");\n const result = await runDailyProactiveChecks(DATA_DIR);\n logger.info(\"proactive\", \"daily check\", {\n customersChecked: result.customersChecked,\n tasksEnqueued: result.tasksEnqueued,\n });\n if (result.errors.length > 0) {\n logger.warn(\"proactive\", \"errors during daily check\", { errors: result.errors });\n }\n const { drainProactiveQueue } = await import(\"../core/notification-dispatcher.js\");\n const drain = await drainProactiveQueue(DATA_DIR);\n logger.info(\"proactive\", \"dispatched tasks\", { sent: drain.sent, failed: drain.failed });\n const { syncGoalProgressFromPipeline } = await import(\"../core/goal-engine.js\");\n const goalSync = await syncGoalProgressFromPipeline(DATA_DIR);\n if (goalSync.updated.length > 0) {\n logger.info(\"goals\", \"progress synced\", { updated: goalSync.updated });\n }\n } catch (err) {\n logger.error(\"proactive\", \"daily check failed\", { error: (err as Error).message });\n }\n },\n null,\n true,\n undefined,\n null,\n false,\n undefined,\n false, // unrefTimeout — keep event loop alive\n true // waitForCompletion\n);\n\n// SLA breach check — daily at 08:00\nnew CronJob(\n \"0 8 * * *\",\n async () => {\n try {\n const { checkSlaBreaches } = await import(\"../core/sla-engine.js\");\n const today = new Date().toISOString().slice(0, 10);\n const breaches = await checkSlaBreaches(DATA_DIR, today);\n if (breaches.length > 0) {\n logger.warn(\"tickets\", \"SLA breaches found\", { count: breaches.length });\n for (const { slug, ticket } of breaches) {\n logger.warn(\"tickets\", \"SLA breach\", {\n slug,\n ticketId: ticket.id,\n title: ticket.title,\n due: ticket.slaDue,\n });\n }\n }\n } catch (err) {\n logger.error(\"tickets\", \"SLA check failed\", { error: (err as Error).message });\n }\n },\n null,\n true,\n undefined,\n null,\n false,\n undefined,\n false, // unrefTimeout — keep event loop alive\n true // waitForCompletion\n);\n\n// Email sequence cycle — every 6 hours\nnew CronJob(\n \"0 */6 * * *\",\n async () => {\n try {\n const { runSequenceCycle } = await import(\"../core/sequence-engine.js\");\n const today = new Date().toISOString().slice(0, 10);\n const result = await runSequenceCycle(DATA_DIR, today);\n logger.info(\"sequences\", \"cycle complete\", {\n sent: result.sent,\n completed: result.completed,\n errors: result.errors.length,\n });\n } catch (err) {\n logger.error(\"sequences\", \"cycle failed\", { error: (err as Error).message });\n }\n },\n null,\n true,\n undefined,\n null,\n false,\n undefined,\n false, // unrefTimeout — keep event loop alive\n true // waitForCompletion\n);\n\nawait startWatcher();\n\n// Signal ready\nif (process.send) process.send(\"ready\");\nlogger.info(\"daemon\", \"daemon started\");\n"],"mappings":"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"}
@@ -265,6 +265,6 @@ function directionFor(msg, user) {
265
265
  return (msg.from.match(/<([^>]+)>/)?.[1] ?? msg.from).toLowerCase() === user.toLowerCase() ? "outbound" : "inbound";
266
266
  }
267
267
  //#endregion
268
- export { syncImapMailbox };
268
+ export { syncImapMailbox as n, normalizeParsedEmail as t };
269
269
 
270
- //# sourceMappingURL=imap-o6PRuBvm.js.map
270
+ //# sourceMappingURL=imap-BRgNh3T3.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"imap-o6PRuBvm.js","names":[],"sources":["../src/sync/email-ingest.ts","../src/sync/email-router.ts","../src/sync/connectors/imap.ts"],"sourcesContent":["// src/sync/email-ingest.ts\nimport { appendInteraction } from \"../fs/interactions-writer.js\";\nimport { persistAttachment, DEFAULT_MAX_ATTACHMENT_BYTES } from \"./attachments.js\";\nimport { chunkText } from \"../core/chunk.js\";\nimport { logger } from \"../core/logger.js\";\n\n/** An attachment with its bytes already in hand (no provider fetch needed). */\nexport interface NormalizedAttachment {\n filename: string;\n mimeType: string;\n content: Buffer;\n}\n\n/**\n * A provider-independent email, normalized from Gmail / IMAP / Graph into a\n * single shape so the downstream pipeline (summary, attachments→Markdown,\n * indexing, interaction log) is written once and reused everywhere.\n */\nexport interface NormalizedEmail {\n /** Stable id for dedup and as the attachment filename prefix. */\n messageId: string;\n /** Raw `From` header value (display name + address). */\n from: string;\n /** Recipient addresses (to + cc), lowercased. */\n toAddresses: string[];\n subject: string;\n /** Message date as YYYY-MM-DD. */\n date: string;\n /** Body already rendered to Markdown (plain verbatim or HTML→MD). */\n bodyMarkdown: string;\n attachments: NormalizedAttachment[];\n /** Canonical source ref, e.g. `imap://user@host/INBOX/42`. */\n sourceRef: string;\n}\n\nexport interface IngestOptions {\n includeAttachments?: boolean;\n maxAttachmentBytes?: number;\n direction?: \"inbound\" | \"outbound\";\n}\n\n/**\n * Ingest one normalized email into a customer: convert + index its\n * attachments, summarize it, append the interaction (with attachment links),\n * and index the full body (chunked) for semantic search. Caller is responsible\n * for deduplication (skip messages whose sourceRef is already logged).\n */\nexport async function ingestEmail(\n dataDir: string,\n slug: string,\n msg: NormalizedEmail,\n options: IngestOptions = {}\n): Promise<{ attachments: number; chunks: number }> {\n const includeAttachments = options.includeAttachments ?? true;\n const maxBytes = options.maxAttachmentBytes ?? DEFAULT_MAX_ATTACHMENT_BYTES;\n\n // Attachments first, so the interaction entry can link to the Markdown.\n const attachmentLinks: string[] = [];\n if (includeAttachments) {\n for (const att of msg.attachments) {\n if (att.content.length > maxBytes) {\n logger.warn(\"email-ingest\", \"skipping oversized attachment\", {\n filename: att.filename,\n bytes: att.content.length,\n });\n continue;\n }\n try {\n const saved = await persistAttachment({\n dataDir,\n slug,\n messageId: msg.messageId,\n source: msg.sourceRef,\n date: msg.date,\n filename: att.filename,\n mimeType: att.mimeType,\n buffer: att.content,\n });\n attachmentLinks.push(saved.markdownName);\n } catch (err) {\n logger.warn(\"email-ingest\", \"attachment failed\", {\n filename: att.filename,\n error: (err as Error).message,\n });\n }\n }\n }\n\n // LLM summary — non-blocking fallback to the raw body when no API key.\n const { summarizeEmail } = await import(\"../core/llm.js\");\n const summary = await summarizeEmail(msg.subject, msg.bodyMarkdown, msg.from);\n\n await appendInteraction(dataDir, slug, {\n date: msg.date,\n type: \"Email\",\n direction: options.direction ?? \"inbound\",\n with: msg.from,\n subject: msg.subject,\n summary: summary.summary,\n nextSteps: summary.nextSteps,\n ...(attachmentLinks.length > 0 ? { attachments: attachmentLinks } : {}),\n sourceRef: msg.sourceRef,\n synced: new Date().toISOString(),\n });\n\n // Index the full email (subject + body), chunked for long threads.\n const { indexInLanceDB } = await import(\"../core/lancedb.js\");\n const bodyChunks = chunkText(`${msg.subject}\\n${msg.bodyMarkdown}`);\n for (let i = 0; i < bodyChunks.length; i++) {\n const ref = i === 0 ? msg.sourceRef : `${msg.sourceRef}#${i}`;\n await indexInLanceDB(dataDir, slug, bodyChunks[i]!, ref, {\n date: msg.date,\n type: \"Email\",\n }).catch((err: unknown) => {\n logger.error(\"email-ingest\", \"LanceDB index failed\", { error: (err as Error).message });\n });\n }\n\n return { attachments: attachmentLinks.length, chunks: bodyChunks.length };\n}\n","// src/sync/email-router.ts\nimport fs from \"fs\";\nimport path from \"path\";\nimport matter from \"gray-matter\";\nimport { listCustomerSlugs } from \"../fs/customer-dir.js\";\n\nexport interface CustomerRoutingInfo {\n slug: string;\n /** Lowercased domains that identify this customer (e.g. \"acme.com\"). */\n domains: string[];\n /** Lowercased full email addresses that identify this customer. */\n emails: string[];\n}\n\n/** Extract the bare email address from a header value like `\"Name <a@b.com>\"`. */\nexport function extractEmailAddress(headerValue: string): string {\n const angle = headerValue.match(/<([^>]+)>/);\n const raw = (angle ? angle[1] : headerValue) ?? \"\";\n return raw.trim().toLowerCase();\n}\n\n/** The domain part of an email address, lowercased (empty string if malformed). */\nexport function domainOf(email: string): string {\n const at = email.lastIndexOf(\"@\");\n return at >= 0 ? email.slice(at + 1).trim().toLowerCase() : \"\";\n}\n\n/**\n * Split a header that may contain several comma-separated addresses\n * (To/Cc) into individual lowercased email addresses.\n */\nexport function parseAddressList(headerValue: string | undefined): string[] {\n if (!headerValue) return [];\n return headerValue\n .split(\",\")\n .map((part) => extractEmailAddress(part))\n .filter((a) => a.includes(\"@\"));\n}\n\n/** Read just the routing-relevant fields from a customer's main_facts (tolerant). */\nfunction readRoutingFields(\n dataDir: string,\n slug: string\n): { domain?: string | undefined; email?: string | undefined; primary_contact?: string | undefined } {\n const file = path.join(dataDir, \"customers\", slug, \"main_facts.md\");\n if (!fs.existsSync(file)) return {};\n try {\n const data = matter(fs.readFileSync(file, \"utf-8\")).data as Record<string, unknown>;\n return {\n domain: typeof data[\"domain\"] === \"string\" ? data[\"domain\"] : undefined,\n email: typeof data[\"email\"] === \"string\" ? data[\"email\"] : undefined,\n primary_contact:\n typeof data[\"primary_contact\"] === \"string\" ? data[\"primary_contact\"] : undefined,\n };\n } catch {\n return {};\n }\n}\n\n/**\n * Build the routing table from every customer's main_facts. A customer is\n * identified by its `domain`, `email`, and `primary_contact` (when it looks\n * like an email). Customers without any identifier are still listed (empty\n * arrays) so callers can see them, but they never match.\n */\nexport function buildRoutingTable(dataDir: string): CustomerRoutingInfo[] {\n return listCustomerSlugs(dataDir).map((slug) => {\n const facts = readRoutingFields(dataDir, slug);\n const domains = new Set<string>();\n const emails = new Set<string>();\n if (facts.domain) domains.add(facts.domain.trim().toLowerCase());\n for (const candidate of [facts.email, facts.primary_contact]) {\n if (candidate && candidate.includes(\"@\")) {\n const addr = candidate.trim().toLowerCase();\n emails.add(addr);\n const d = domainOf(addr);\n if (d) domains.add(d);\n }\n }\n return { slug, domains: [...domains], emails: [...emails] };\n });\n}\n\n/**\n * Route a message to a customer slug by matching any of its addresses\n * (from/to/cc) against the routing table. Exact email matches win over domain\n * matches. Returns the matched slug, or null when nothing matches (the message\n * is \"unrouted\").\n */\nexport function routeMessage(addresses: string[], table: CustomerRoutingInfo[]): string | null {\n const addrs = addresses.map((a) => a.trim().toLowerCase()).filter((a) => a.includes(\"@\"));\n if (addrs.length === 0) return null;\n const domains = new Set(addrs.map(domainOf).filter(Boolean));\n\n // Pass 1: exact email match (most specific).\n for (const c of table) {\n if (c.emails.some((e) => addrs.includes(e))) return c.slug;\n }\n // Pass 2: domain match.\n for (const c of table) {\n if (c.domains.some((d) => domains.has(d))) return c.slug;\n }\n return null;\n}\n","// src/sync/connectors/imap.ts\nimport { readInteractions } from \"../../fs/interactions-writer.js\";\nimport { ingestEmail, type NormalizedEmail } from \"../email-ingest.js\";\nimport { buildRoutingTable, routeMessage, domainOf } from \"../email-router.js\";\nimport { htmlToMarkdown } from \"../converters/html.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport interface ImapMailboxConfig {\n host: string;\n port?: number;\n secure?: boolean;\n /** Either a password (legacy IMAP) or an OAuth2 access token (XOAUTH2). */\n auth: { user: string; pass?: string; accessToken?: string };\n mailbox?: string;\n}\n\n/** Minimal slice of the ImapFlow client surface we depend on (for testability). */\nexport interface ImapMessage {\n uid: number;\n source: Buffer;\n}\nexport interface ImapClient {\n connect(): Promise<void>;\n getMailboxLock(mailbox: string): Promise<{ release: () => void }>;\n fetch(\n range: unknown,\n query: { uid?: boolean; source?: boolean }\n ): AsyncIterable<ImapMessage>;\n logout(): Promise<void>;\n}\n\nexport interface SyncImapOptions {\n dataDir: string;\n config: ImapMailboxConfig;\n since?: Date;\n /** Fixed customer slug. When omitted, messages are auto-routed by domain. */\n slug?: string;\n includeAttachments?: boolean;\n maxAttachmentBytes?: number;\n /** Inject a client (tests); defaults to a real ImapFlow connection. */\n clientFactory?: (config: ImapMailboxConfig) => ImapClient;\n}\n\nexport interface SyncImapResult {\n synced: number;\n skipped: number;\n unrouted: number;\n}\n\n/** Build a real ImapFlow client. Loaded lazily so the dep stays off hot paths. */\nasync function defaultClientFactory(config: ImapMailboxConfig): Promise<ImapClient> {\n const { ImapFlow } = await import(\"imapflow\");\n const auth = config.auth.accessToken\n ? { user: config.auth.user, accessToken: config.auth.accessToken }\n : { user: config.auth.user, pass: config.auth.pass ?? \"\" };\n return new ImapFlow({\n host: config.host,\n port: config.port ?? 993,\n secure: config.secure ?? true,\n auth,\n logger: false,\n }) as unknown as ImapClient;\n}\n\n/** Fields extracted from a parsed message, decoupled from mailparser's types. */\nexport interface ParsedEmailInput {\n messageId?: string | undefined;\n fromText?: string | undefined;\n toAddresses?: string[] | undefined;\n subject?: string | undefined;\n date?: Date | undefined;\n text?: string | undefined;\n html?: string | false | undefined;\n attachments?:\n | Array<{ filename?: string | undefined; contentType?: string | undefined; content: Buffer }>\n | undefined;\n}\n\n/** Normalize extracted email fields into the provider-independent email shape. */\nexport async function normalizeParsedEmail(\n parsed: ParsedEmailInput,\n ctx: { user: string; host: string; mailbox: string; uid: number }\n): Promise<NormalizedEmail> {\n const toAddresses = (parsed.toAddresses ?? [])\n .map((a) => a.toLowerCase())\n .filter((a) => a.includes(\"@\"));\n\n const plain = (parsed.text ?? \"\").trim();\n const bodyMarkdown = plain\n ? plain\n : parsed.html\n ? (await htmlToMarkdown(parsed.html)).trim()\n : \"\";\n\n const rawId = (parsed.messageId ?? \"\").replace(/[<>]/g, \"\").trim();\n const messageId = rawId || `uid-${ctx.uid}`;\n\n return {\n messageId,\n from: parsed.fromText ?? \"\",\n toAddresses,\n subject: parsed.subject ?? \"(no subject)\",\n date: (parsed.date ?? new Date()).toISOString().slice(0, 10),\n bodyMarkdown,\n attachments: (parsed.attachments ?? [])\n .filter((a) => a.filename)\n .map((a) => ({\n filename: a.filename!,\n mimeType: a.contentType ?? \"application/octet-stream\",\n content: a.content,\n })),\n sourceRef: `imap://${ctx.user}@${ctx.host}/${ctx.mailbox}/${ctx.uid}`,\n };\n}\n\n/** Flatten mailparser's AddressObject | AddressObject[] | undefined to addresses. */\nfunction flattenAddresses(\n field:\n | { value?: Array<{ address?: string | undefined }> }\n | Array<{ value?: Array<{ address?: string | undefined }> }>\n | undefined\n): string[] {\n if (!field) return [];\n const objects = Array.isArray(field) ? field : [field];\n return objects\n .flatMap((o) => o.value ?? [])\n .map((a) => (a.address ?? \"\").toLowerCase())\n .filter((a) => a.includes(\"@\"));\n}\n\n/**\n * Sync a whole IMAP mailbox (any provider). Each message is parsed, routed to a\n * customer — by a fixed `slug` or auto-routed by sender/recipient domain — and\n * ingested through the shared pipeline (attachments→Markdown, summary, index).\n * Messages that match no customer are counted as `unrouted` and skipped.\n */\nexport async function syncImapMailbox(opts: SyncImapOptions): Promise<SyncImapResult> {\n const result: SyncImapResult = { synced: 0, skipped: 0, unrouted: 0 };\n const mailbox = opts.config.mailbox ?? \"INBOX\";\n const { simpleParser } = await import(\"mailparser\");\n\n const client = opts.clientFactory\n ? opts.clientFactory(opts.config)\n : await defaultClientFactory(opts.config);\n\n // Routing table (auto-route mode) + per-slug dedup cache.\n const table = opts.slug ? null : buildRoutingTable(opts.dataDir);\n const dedupCache = new Map<string, string>();\n const seen = async (slug: string, sourceRef: string): Promise<boolean> => {\n let content = dedupCache.get(slug);\n if (content === undefined) {\n content = await readInteractions(opts.dataDir, slug).catch(() => \"\");\n dedupCache.set(slug, content);\n }\n return content.includes(sourceRef);\n };\n\n await client.connect();\n const lock = await client.getMailboxLock(mailbox);\n try {\n const range = opts.since ? { since: opts.since } : { all: true };\n for await (const message of client.fetch(range, { uid: true, source: true })) {\n try {\n const parsed = await simpleParser(message.source);\n const msg = await normalizeParsedEmail(\n {\n messageId: parsed.messageId,\n fromText: parsed.from?.text,\n toAddresses: [...flattenAddresses(parsed.to), ...flattenAddresses(parsed.cc)],\n subject: parsed.subject,\n date: parsed.date,\n text: parsed.text,\n html: parsed.html,\n attachments: parsed.attachments,\n },\n {\n user: opts.config.auth.user,\n host: opts.config.host,\n mailbox,\n uid: message.uid,\n }\n );\n\n // Route: fixed slug, or auto-route by any from/to/cc domain.\n let slug = opts.slug ?? null;\n if (!slug && table) {\n const fromAddr = (msg.from.match(/<([^>]+)>/)?.[1] ?? msg.from).toLowerCase();\n const addresses = [fromAddr, ...msg.toAddresses].filter((a) => domainOf(a));\n slug = routeMessage(addresses, table);\n }\n if (!slug) {\n result.unrouted++;\n continue;\n }\n\n if (await seen(slug, msg.sourceRef)) {\n result.skipped++;\n continue;\n }\n\n await ingestEmail(opts.dataDir, slug, msg, {\n ...(opts.includeAttachments !== undefined\n ? { includeAttachments: opts.includeAttachments }\n : {}),\n ...(opts.maxAttachmentBytes !== undefined\n ? { maxAttachmentBytes: opts.maxAttachmentBytes }\n : {}),\n direction: directionFor(msg, opts.config.auth.user),\n });\n dedupCache.set(slug, (dedupCache.get(slug) ?? \"\") + msg.sourceRef);\n result.synced++;\n } catch (err) {\n logger.warn(\"imap-sync\", \"message failed\", {\n uid: message.uid,\n error: (err as Error).message,\n });\n result.skipped++;\n }\n }\n } finally {\n lock.release();\n await client.logout().catch(() => undefined);\n }\n\n return result;\n}\n\n/** Inbound unless the mailbox owner is the sender. */\nfunction directionFor(msg: NormalizedEmail, user: string): \"inbound\" | \"outbound\" {\n const fromAddr = (msg.from.match(/<([^>]+)>/)?.[1] ?? msg.from).toLowerCase();\n return fromAddr === user.toLowerCase() ? \"outbound\" : \"inbound\";\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"imap-BRgNh3T3.js","names":[],"sources":["../src/sync/email-ingest.ts","../src/sync/email-router.ts","../src/sync/connectors/imap.ts"],"sourcesContent":["// src/sync/email-ingest.ts\nimport { appendInteraction } from \"../fs/interactions-writer.js\";\nimport { persistAttachment, DEFAULT_MAX_ATTACHMENT_BYTES } from \"./attachments.js\";\nimport { chunkText } from \"../core/chunk.js\";\nimport { logger } from \"../core/logger.js\";\n\n/** An attachment with its bytes already in hand (no provider fetch needed). */\nexport interface NormalizedAttachment {\n filename: string;\n mimeType: string;\n content: Buffer;\n}\n\n/**\n * A provider-independent email, normalized from Gmail / IMAP / Graph into a\n * single shape so the downstream pipeline (summary, attachments→Markdown,\n * indexing, interaction log) is written once and reused everywhere.\n */\nexport interface NormalizedEmail {\n /** Stable id for dedup and as the attachment filename prefix. */\n messageId: string;\n /** Raw `From` header value (display name + address). */\n from: string;\n /** Recipient addresses (to + cc), lowercased. */\n toAddresses: string[];\n subject: string;\n /** Message date as YYYY-MM-DD. */\n date: string;\n /** Body already rendered to Markdown (plain verbatim or HTML→MD). */\n bodyMarkdown: string;\n attachments: NormalizedAttachment[];\n /** Canonical source ref, e.g. `imap://user@host/INBOX/42`. */\n sourceRef: string;\n}\n\nexport interface IngestOptions {\n includeAttachments?: boolean;\n maxAttachmentBytes?: number;\n direction?: \"inbound\" | \"outbound\";\n}\n\n/**\n * Ingest one normalized email into a customer: convert + index its\n * attachments, summarize it, append the interaction (with attachment links),\n * and index the full body (chunked) for semantic search. Caller is responsible\n * for deduplication (skip messages whose sourceRef is already logged).\n */\nexport async function ingestEmail(\n dataDir: string,\n slug: string,\n msg: NormalizedEmail,\n options: IngestOptions = {}\n): Promise<{ attachments: number; chunks: number }> {\n const includeAttachments = options.includeAttachments ?? true;\n const maxBytes = options.maxAttachmentBytes ?? DEFAULT_MAX_ATTACHMENT_BYTES;\n\n // Attachments first, so the interaction entry can link to the Markdown.\n const attachmentLinks: string[] = [];\n if (includeAttachments) {\n for (const att of msg.attachments) {\n if (att.content.length > maxBytes) {\n logger.warn(\"email-ingest\", \"skipping oversized attachment\", {\n filename: att.filename,\n bytes: att.content.length,\n });\n continue;\n }\n try {\n const saved = await persistAttachment({\n dataDir,\n slug,\n messageId: msg.messageId,\n source: msg.sourceRef,\n date: msg.date,\n filename: att.filename,\n mimeType: att.mimeType,\n buffer: att.content,\n });\n attachmentLinks.push(saved.markdownName);\n } catch (err) {\n logger.warn(\"email-ingest\", \"attachment failed\", {\n filename: att.filename,\n error: (err as Error).message,\n });\n }\n }\n }\n\n // LLM summary — non-blocking fallback to the raw body when no API key.\n const { summarizeEmail } = await import(\"../core/llm.js\");\n const summary = await summarizeEmail(msg.subject, msg.bodyMarkdown, msg.from);\n\n await appendInteraction(dataDir, slug, {\n date: msg.date,\n type: \"Email\",\n direction: options.direction ?? \"inbound\",\n with: msg.from,\n subject: msg.subject,\n summary: summary.summary,\n nextSteps: summary.nextSteps,\n ...(attachmentLinks.length > 0 ? { attachments: attachmentLinks } : {}),\n sourceRef: msg.sourceRef,\n synced: new Date().toISOString(),\n });\n\n // Index the full email (subject + body), chunked for long threads.\n const { indexInLanceDB } = await import(\"../core/lancedb.js\");\n const bodyChunks = chunkText(`${msg.subject}\\n${msg.bodyMarkdown}`);\n for (let i = 0; i < bodyChunks.length; i++) {\n const ref = i === 0 ? msg.sourceRef : `${msg.sourceRef}#${i}`;\n await indexInLanceDB(dataDir, slug, bodyChunks[i]!, ref, {\n date: msg.date,\n type: \"Email\",\n }).catch((err: unknown) => {\n logger.error(\"email-ingest\", \"LanceDB index failed\", { error: (err as Error).message });\n });\n }\n\n return { attachments: attachmentLinks.length, chunks: bodyChunks.length };\n}\n","// src/sync/email-router.ts\nimport fs from \"fs\";\nimport path from \"path\";\nimport matter from \"gray-matter\";\nimport { listCustomerSlugs } from \"../fs/customer-dir.js\";\n\nexport interface CustomerRoutingInfo {\n slug: string;\n /** Lowercased domains that identify this customer (e.g. \"acme.com\"). */\n domains: string[];\n /** Lowercased full email addresses that identify this customer. */\n emails: string[];\n}\n\n/** Extract the bare email address from a header value like `\"Name <a@b.com>\"`. */\nexport function extractEmailAddress(headerValue: string): string {\n const angle = headerValue.match(/<([^>]+)>/);\n const raw = (angle ? angle[1] : headerValue) ?? \"\";\n return raw.trim().toLowerCase();\n}\n\n/** The domain part of an email address, lowercased (empty string if malformed). */\nexport function domainOf(email: string): string {\n const at = email.lastIndexOf(\"@\");\n return at >= 0 ? email.slice(at + 1).trim().toLowerCase() : \"\";\n}\n\n/**\n * Split a header that may contain several comma-separated addresses\n * (To/Cc) into individual lowercased email addresses.\n */\nexport function parseAddressList(headerValue: string | undefined): string[] {\n if (!headerValue) return [];\n return headerValue\n .split(\",\")\n .map((part) => extractEmailAddress(part))\n .filter((a) => a.includes(\"@\"));\n}\n\n/** Read just the routing-relevant fields from a customer's main_facts (tolerant). */\nfunction readRoutingFields(\n dataDir: string,\n slug: string\n): { domain?: string | undefined; email?: string | undefined; primary_contact?: string | undefined } {\n const file = path.join(dataDir, \"customers\", slug, \"main_facts.md\");\n if (!fs.existsSync(file)) return {};\n try {\n const data = matter(fs.readFileSync(file, \"utf-8\")).data as Record<string, unknown>;\n return {\n domain: typeof data[\"domain\"] === \"string\" ? data[\"domain\"] : undefined,\n email: typeof data[\"email\"] === \"string\" ? data[\"email\"] : undefined,\n primary_contact:\n typeof data[\"primary_contact\"] === \"string\" ? data[\"primary_contact\"] : undefined,\n };\n } catch {\n return {};\n }\n}\n\n/**\n * Build the routing table from every customer's main_facts. A customer is\n * identified by its `domain`, `email`, and `primary_contact` (when it looks\n * like an email). Customers without any identifier are still listed (empty\n * arrays) so callers can see them, but they never match.\n */\nexport function buildRoutingTable(dataDir: string): CustomerRoutingInfo[] {\n return listCustomerSlugs(dataDir).map((slug) => {\n const facts = readRoutingFields(dataDir, slug);\n const domains = new Set<string>();\n const emails = new Set<string>();\n if (facts.domain) domains.add(facts.domain.trim().toLowerCase());\n for (const candidate of [facts.email, facts.primary_contact]) {\n if (candidate && candidate.includes(\"@\")) {\n const addr = candidate.trim().toLowerCase();\n emails.add(addr);\n const d = domainOf(addr);\n if (d) domains.add(d);\n }\n }\n return { slug, domains: [...domains], emails: [...emails] };\n });\n}\n\n/**\n * Route a message to a customer slug by matching any of its addresses\n * (from/to/cc) against the routing table. Exact email matches win over domain\n * matches. Returns the matched slug, or null when nothing matches (the message\n * is \"unrouted\").\n */\nexport function routeMessage(addresses: string[], table: CustomerRoutingInfo[]): string | null {\n const addrs = addresses.map((a) => a.trim().toLowerCase()).filter((a) => a.includes(\"@\"));\n if (addrs.length === 0) return null;\n const domains = new Set(addrs.map(domainOf).filter(Boolean));\n\n // Pass 1: exact email match (most specific).\n for (const c of table) {\n if (c.emails.some((e) => addrs.includes(e))) return c.slug;\n }\n // Pass 2: domain match.\n for (const c of table) {\n if (c.domains.some((d) => domains.has(d))) return c.slug;\n }\n return null;\n}\n","// src/sync/connectors/imap.ts\nimport { readInteractions } from \"../../fs/interactions-writer.js\";\nimport { ingestEmail, type NormalizedEmail } from \"../email-ingest.js\";\nimport { buildRoutingTable, routeMessage, domainOf } from \"../email-router.js\";\nimport { htmlToMarkdown } from \"../converters/html.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport interface ImapMailboxConfig {\n host: string;\n port?: number;\n secure?: boolean;\n /** Either a password (legacy IMAP) or an OAuth2 access token (XOAUTH2). */\n auth: { user: string; pass?: string; accessToken?: string };\n mailbox?: string;\n}\n\n/** Minimal slice of the ImapFlow client surface we depend on (for testability). */\nexport interface ImapMessage {\n uid: number;\n source: Buffer;\n}\nexport interface ImapClient {\n connect(): Promise<void>;\n getMailboxLock(mailbox: string): Promise<{ release: () => void }>;\n fetch(\n range: unknown,\n query: { uid?: boolean; source?: boolean }\n ): AsyncIterable<ImapMessage>;\n logout(): Promise<void>;\n}\n\nexport interface SyncImapOptions {\n dataDir: string;\n config: ImapMailboxConfig;\n since?: Date;\n /** Fixed customer slug. When omitted, messages are auto-routed by domain. */\n slug?: string;\n includeAttachments?: boolean;\n maxAttachmentBytes?: number;\n /** Inject a client (tests); defaults to a real ImapFlow connection. */\n clientFactory?: (config: ImapMailboxConfig) => ImapClient;\n}\n\nexport interface SyncImapResult {\n synced: number;\n skipped: number;\n unrouted: number;\n}\n\n/** Build a real ImapFlow client. Loaded lazily so the dep stays off hot paths. */\nasync function defaultClientFactory(config: ImapMailboxConfig): Promise<ImapClient> {\n const { ImapFlow } = await import(\"imapflow\");\n const auth = config.auth.accessToken\n ? { user: config.auth.user, accessToken: config.auth.accessToken }\n : { user: config.auth.user, pass: config.auth.pass ?? \"\" };\n return new ImapFlow({\n host: config.host,\n port: config.port ?? 993,\n secure: config.secure ?? true,\n auth,\n logger: false,\n }) as unknown as ImapClient;\n}\n\n/** Fields extracted from a parsed message, decoupled from mailparser's types. */\nexport interface ParsedEmailInput {\n messageId?: string | undefined;\n fromText?: string | undefined;\n toAddresses?: string[] | undefined;\n subject?: string | undefined;\n date?: Date | undefined;\n text?: string | undefined;\n html?: string | false | undefined;\n attachments?:\n | Array<{ filename?: string | undefined; contentType?: string | undefined; content: Buffer }>\n | undefined;\n}\n\n/** Normalize extracted email fields into the provider-independent email shape. */\nexport async function normalizeParsedEmail(\n parsed: ParsedEmailInput,\n ctx: { user: string; host: string; mailbox: string; uid: number }\n): Promise<NormalizedEmail> {\n const toAddresses = (parsed.toAddresses ?? [])\n .map((a) => a.toLowerCase())\n .filter((a) => a.includes(\"@\"));\n\n const plain = (parsed.text ?? \"\").trim();\n const bodyMarkdown = plain\n ? plain\n : parsed.html\n ? (await htmlToMarkdown(parsed.html)).trim()\n : \"\";\n\n const rawId = (parsed.messageId ?? \"\").replace(/[<>]/g, \"\").trim();\n const messageId = rawId || `uid-${ctx.uid}`;\n\n return {\n messageId,\n from: parsed.fromText ?? \"\",\n toAddresses,\n subject: parsed.subject ?? \"(no subject)\",\n date: (parsed.date ?? new Date()).toISOString().slice(0, 10),\n bodyMarkdown,\n attachments: (parsed.attachments ?? [])\n .filter((a) => a.filename)\n .map((a) => ({\n filename: a.filename!,\n mimeType: a.contentType ?? \"application/octet-stream\",\n content: a.content,\n })),\n sourceRef: `imap://${ctx.user}@${ctx.host}/${ctx.mailbox}/${ctx.uid}`,\n };\n}\n\n/** Flatten mailparser's AddressObject | AddressObject[] | undefined to addresses. */\nfunction flattenAddresses(\n field:\n | { value?: Array<{ address?: string | undefined }> }\n | Array<{ value?: Array<{ address?: string | undefined }> }>\n | undefined\n): string[] {\n if (!field) return [];\n const objects = Array.isArray(field) ? field : [field];\n return objects\n .flatMap((o) => o.value ?? [])\n .map((a) => (a.address ?? \"\").toLowerCase())\n .filter((a) => a.includes(\"@\"));\n}\n\n/**\n * Sync a whole IMAP mailbox (any provider). Each message is parsed, routed to a\n * customer — by a fixed `slug` or auto-routed by sender/recipient domain — and\n * ingested through the shared pipeline (attachments→Markdown, summary, index).\n * Messages that match no customer are counted as `unrouted` and skipped.\n */\nexport async function syncImapMailbox(opts: SyncImapOptions): Promise<SyncImapResult> {\n const result: SyncImapResult = { synced: 0, skipped: 0, unrouted: 0 };\n const mailbox = opts.config.mailbox ?? \"INBOX\";\n const { simpleParser } = await import(\"mailparser\");\n\n const client = opts.clientFactory\n ? opts.clientFactory(opts.config)\n : await defaultClientFactory(opts.config);\n\n // Routing table (auto-route mode) + per-slug dedup cache.\n const table = opts.slug ? null : buildRoutingTable(opts.dataDir);\n const dedupCache = new Map<string, string>();\n const seen = async (slug: string, sourceRef: string): Promise<boolean> => {\n let content = dedupCache.get(slug);\n if (content === undefined) {\n content = await readInteractions(opts.dataDir, slug).catch(() => \"\");\n dedupCache.set(slug, content);\n }\n return content.includes(sourceRef);\n };\n\n await client.connect();\n const lock = await client.getMailboxLock(mailbox);\n try {\n const range = opts.since ? { since: opts.since } : { all: true };\n for await (const message of client.fetch(range, { uid: true, source: true })) {\n try {\n const parsed = await simpleParser(message.source);\n const msg = await normalizeParsedEmail(\n {\n messageId: parsed.messageId,\n fromText: parsed.from?.text,\n toAddresses: [...flattenAddresses(parsed.to), ...flattenAddresses(parsed.cc)],\n subject: parsed.subject,\n date: parsed.date,\n text: parsed.text,\n html: parsed.html,\n attachments: parsed.attachments,\n },\n {\n user: opts.config.auth.user,\n host: opts.config.host,\n mailbox,\n uid: message.uid,\n }\n );\n\n // Route: fixed slug, or auto-route by any from/to/cc domain.\n let slug = opts.slug ?? null;\n if (!slug && table) {\n const fromAddr = (msg.from.match(/<([^>]+)>/)?.[1] ?? msg.from).toLowerCase();\n const addresses = [fromAddr, ...msg.toAddresses].filter((a) => domainOf(a));\n slug = routeMessage(addresses, table);\n }\n if (!slug) {\n result.unrouted++;\n continue;\n }\n\n if (await seen(slug, msg.sourceRef)) {\n result.skipped++;\n continue;\n }\n\n await ingestEmail(opts.dataDir, slug, msg, {\n ...(opts.includeAttachments !== undefined\n ? { includeAttachments: opts.includeAttachments }\n : {}),\n ...(opts.maxAttachmentBytes !== undefined\n ? { maxAttachmentBytes: opts.maxAttachmentBytes }\n : {}),\n direction: directionFor(msg, opts.config.auth.user),\n });\n dedupCache.set(slug, (dedupCache.get(slug) ?? \"\") + msg.sourceRef);\n result.synced++;\n } catch (err) {\n logger.warn(\"imap-sync\", \"message failed\", {\n uid: message.uid,\n error: (err as Error).message,\n });\n result.skipped++;\n }\n }\n } finally {\n lock.release();\n await client.logout().catch(() => undefined);\n }\n\n return result;\n}\n\n/** Inbound unless the mailbox owner is the sender. */\nfunction directionFor(msg: NormalizedEmail, user: string): \"inbound\" | \"outbound\" {\n const fromAddr = (msg.from.match(/<([^>]+)>/)?.[1] ?? msg.from).toLowerCase();\n return fromAddr === user.toLowerCase() ? \"outbound\" : \"inbound\";\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { n as syncImapMailbox } from "./imap-BRgNh3T3.js";
2
+ export { syncImapMailbox };
@@ -96,12 +96,12 @@ declare const MainFactsSchema: z.ZodObject<{
96
96
  created: z.ZodEffects<z.ZodString, string, unknown>;
97
97
  updated: z.ZodEffects<z.ZodString, string, unknown>;
98
98
  }, "strip", z.ZodTypeAny, {
99
- created: string;
100
99
  name: string;
101
- relationship_stage: "prospect" | "active" | "churned" | "paused";
102
100
  currency: string;
103
- tags: string[];
104
101
  updated: string;
102
+ created: string;
103
+ relationship_stage: "prospect" | "active" | "churned" | "paused";
104
+ tags: string[];
105
105
  domain?: string | undefined;
106
106
  email?: string | undefined;
107
107
  phone?: string | undefined;
@@ -112,17 +112,17 @@ declare const MainFactsSchema: z.ZodObject<{
112
112
  }, {
113
113
  name: string;
114
114
  relationship_stage: "prospect" | "active" | "churned" | "paused";
115
+ currency?: string | undefined;
116
+ updated?: unknown;
115
117
  created?: unknown;
116
118
  domain?: string | undefined;
117
119
  email?: string | undefined;
118
120
  phone?: string | undefined;
119
121
  industry?: string | undefined;
120
122
  deal_value?: number | undefined;
121
- currency?: string | undefined;
122
123
  primary_contact?: string | undefined;
123
124
  timezone?: string | undefined;
124
125
  tags?: string[] | undefined;
125
- updated?: unknown;
126
126
  }>;
127
127
  type MainFacts = z.infer<typeof MainFactsSchema>;
128
128
  //# sourceMappingURL=main-facts.d.ts.map
@@ -178,17 +178,17 @@ declare const PipelineDealSchema: z.ZodObject<{
178
178
  updated: z.ZodString;
179
179
  }, "strip", z.ZodTypeAny, {
180
180
  name: string;
181
+ stage: "lead" | "qualified" | "proposal" | "negotiation" | "won" | "lost";
181
182
  currency: string;
182
183
  updated: string;
183
- stage: "lead" | "qualified" | "proposal" | "negotiation" | "won" | "lost";
184
184
  value?: number | undefined;
185
185
  probability?: number | undefined;
186
186
  close_date?: string | undefined;
187
187
  notes?: string | undefined;
188
188
  }, {
189
189
  name: string;
190
- updated: string;
191
190
  stage: "lead" | "qualified" | "proposal" | "negotiation" | "won" | "lost";
191
+ updated: string;
192
192
  value?: number | undefined;
193
193
  currency?: string | undefined;
194
194
  probability?: number | undefined;
@@ -544,4 +544,4 @@ declare const VERSION = "0.1.0";
544
544
 
545
545
  //#endregion
546
546
  export { type GlobalSources, type InteractionEntry, type KbArticle, type MainFacts, type PipelineDeal, type QuoteLineItem, type Quote as QuoteRecord, type SurveyDefinition, type SurveyResponse, type TicketPriority, type Ticket as TicketRecord, type TicketStatus, VERSION, canSeeCustomer, clearSession, createCustomer, customerExists, filterAuditLog, getRbacConfig, getRole, getSession, readAuditLog, readMainFacts, runAudit, runBackup, runValidate, setSession };
547
- //# sourceMappingURL=index-BBAlKZg6.d.ts.map
547
+ //# sourceMappingURL=index-DoYT-azq.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-DoYT-azq.d.ts","names":[],"sources":["../src/schemas/sources.ts","../src/schemas/main-facts.ts","../src/schemas/interaction.ts","../src/schemas/pipeline.ts","../src/schemas/ticket.ts","../src/schemas/quote.ts","../src/schemas/kb-article.ts","../src/schemas/survey.ts","../src/commands/create.ts","../src/commands/backup.ts","../src/commands/audit.ts","../src/commands/validate.ts","../src/fs/customer-dir.ts","../src/fs/audit-log.ts","../src/core/rbac.ts","../src/core/session-store.ts","../src/version.ts"],"mappings":"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"}
@@ -42,8 +42,8 @@ declare const GlobalSourcesSchema: z.ZodObject<{
42
42
  version: z.ZodDefault<z.ZodNumber>;
43
43
  created: z.ZodString;
44
44
  }, "strip", z.ZodTypeAny, {
45
- version: number;
46
45
  created: string;
46
+ version: number;
47
47
  gmail?: {
48
48
  type: "gmail";
49
49
  query: string;
@@ -96,9 +96,9 @@ declare const MainFactsSchema: z.ZodObject<{
96
96
  updated: z.ZodEffects<z.ZodString, string, unknown>;
97
97
  }, "strip", z.ZodTypeAny, {
98
98
  created: string;
99
+ currency: string;
99
100
  name: string;
100
101
  relationship_stage: "prospect" | "active" | "churned" | "paused";
101
- currency: string;
102
102
  tags: string[];
103
103
  updated: string;
104
104
  domain?: string | undefined;
@@ -112,12 +112,12 @@ declare const MainFactsSchema: z.ZodObject<{
112
112
  name: string;
113
113
  relationship_stage: "prospect" | "active" | "churned" | "paused";
114
114
  created?: unknown;
115
+ currency?: string | undefined;
115
116
  domain?: string | undefined;
116
117
  email?: string | undefined;
117
118
  phone?: string | undefined;
118
119
  industry?: string | undefined;
119
120
  deal_value?: number | undefined;
120
- currency?: string | undefined;
121
121
  primary_contact?: string | undefined;
122
122
  timezone?: string | undefined;
123
123
  tags?: string[] | undefined;
@@ -176,8 +176,8 @@ declare const PipelineDealSchema: z.ZodObject<{
176
176
  notes: z.ZodOptional<z.ZodString>;
177
177
  updated: z.ZodString;
178
178
  }, "strip", z.ZodTypeAny, {
179
- name: string;
180
179
  currency: string;
180
+ name: string;
181
181
  updated: string;
182
182
  stage: "lead" | "qualified" | "proposal" | "negotiation" | "won" | "lost";
183
183
  value?: number | undefined;
@@ -211,20 +211,20 @@ declare const TicketSchema: z.ZodObject<{
211
211
  resolved: z.ZodOptional<z.ZodString>;
212
212
  description: z.ZodOptional<z.ZodString>;
213
213
  }, "strip", z.ZodTypeAny, {
214
- status: "open" | "in-progress" | "waiting" | "resolved" | "closed";
215
- created: string;
216
214
  id: string;
217
215
  title: string;
216
+ status: "open" | "in-progress" | "waiting" | "resolved" | "closed";
218
217
  priority: "urgent" | "high" | "normal" | "low";
218
+ created: string;
219
219
  resolved?: string | undefined;
220
220
  assignee?: string | undefined;
221
221
  slaDue?: string | undefined;
222
222
  description?: string | undefined;
223
223
  }, {
224
- status: "open" | "in-progress" | "waiting" | "resolved" | "closed";
225
- created: string;
226
224
  id: string;
227
225
  title: string;
226
+ status: "open" | "in-progress" | "waiting" | "resolved" | "closed";
227
+ created: string;
228
228
  resolved?: string | undefined;
229
229
  priority?: "urgent" | "high" | "normal" | "low" | undefined;
230
230
  assignee?: string | undefined;
@@ -287,7 +287,6 @@ declare const QuoteSchema: z.ZodObject<{
287
287
  htmlPath: z.ZodOptional<z.ZodString>;
288
288
  }, "strip", z.ZodTypeAny, {
289
289
  status: "draft" | "sent" | "viewed" | "accepted" | "declined";
290
- currency: string;
291
290
  total: number;
292
291
  quoteNumber: string;
293
292
  slug: string;
@@ -301,6 +300,7 @@ declare const QuoteSchema: z.ZodObject<{
301
300
  subtotal: number;
302
301
  vatPercent: number;
303
302
  vat: number;
303
+ currency: string;
304
304
  createdAt: string;
305
305
  validUntilDays: number;
306
306
  validUntil: string;
@@ -345,10 +345,10 @@ declare const KbArticleSchema: z.ZodObject<{
345
345
  updatedAt: z.ZodString;
346
346
  sourceTicketId: z.ZodOptional<z.ZodString>;
347
347
  }, "strip", z.ZodTypeAny, {
348
- tags: string[];
349
348
  id: string;
350
349
  title: string;
351
350
  createdAt: string;
351
+ tags: string[];
352
352
  category: string;
353
353
  public: boolean;
354
354
  updatedAt: string;
@@ -388,8 +388,8 @@ declare const SurveyDefinitionSchema: z.ZodObject<{
388
388
  commentPrompt: z.ZodOptional<z.ZodString>;
389
389
  createdAt: z.ZodString;
390
390
  }, "strip", z.ZodTypeAny, {
391
- type: "nps" | "csat" | "ces";
392
391
  id: string;
392
+ type: "nps" | "csat" | "ces";
393
393
  createdAt: string;
394
394
  question: string;
395
395
  scale: {
@@ -543,4 +543,4 @@ declare const VERSION = "0.1.0";
543
543
 
544
544
  //#endregion
545
545
  export { type GlobalSources, type InteractionEntry, type KbArticle, type MainFacts, type PipelineDeal, type QuoteLineItem, type Quote as QuoteRecord, type SurveyDefinition, type SurveyResponse, type TicketPriority, type Ticket as TicketRecord, type TicketStatus, VERSION, canSeeCustomer, clearSession, createCustomer, customerExists, filterAuditLog, getRbacConfig, getRole, getSession, readAuditLog, readMainFacts, runAudit, runBackup, runValidate, setSession };
546
- //# sourceMappingURL=index-DNHsURo5.d.cts.map
546
+ //# sourceMappingURL=index-Ewy4f1XW.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-DNHsURo5.d.cts","names":[],"sources":["../src/schemas/sources.ts","../src/schemas/main-facts.ts","../src/schemas/interaction.ts","../src/schemas/pipeline.ts","../src/schemas/ticket.ts","../src/schemas/quote.ts","../src/schemas/kb-article.ts","../src/schemas/survey.ts","../src/commands/create.ts","../src/commands/backup.ts","../src/commands/audit.ts","../src/commands/validate.ts","../src/fs/customer-dir.ts","../src/fs/audit-log.ts","../src/core/rbac.ts","../src/core/session-store.ts","../src/version.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index-Ewy4f1XW.d.cts","names":[],"sources":["../src/schemas/sources.ts","../src/schemas/main-facts.ts","../src/schemas/interaction.ts","../src/schemas/pipeline.ts","../src/schemas/ticket.ts","../src/schemas/quote.ts","../src/schemas/kb-article.ts","../src/schemas/survey.ts","../src/commands/create.ts","../src/commands/backup.ts","../src/commands/audit.ts","../src/commands/validate.ts","../src/fs/customer-dir.ts","../src/fs/audit-log.ts","../src/core/rbac.ts","../src/core/session-store.ts","../src/version.ts"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -42,8 +42,8 @@ declare const GlobalSourcesSchema: z.ZodObject<{
42
42
  version: z.ZodDefault<z.ZodNumber>;
43
43
  created: z.ZodString;
44
44
  }, "strip", z.ZodTypeAny, {
45
- version: number;
46
45
  created: string;
46
+ version: number;
47
47
  gmail?: {
48
48
  type: "gmail";
49
49
  query: string;
@@ -96,9 +96,9 @@ declare const MainFactsSchema: z.ZodObject<{
96
96
  updated: z.ZodEffects<z.ZodString, string, unknown>;
97
97
  }, "strip", z.ZodTypeAny, {
98
98
  created: string;
99
+ currency: string;
99
100
  name: string;
100
101
  relationship_stage: "prospect" | "active" | "churned" | "paused";
101
- currency: string;
102
102
  tags: string[];
103
103
  updated: string;
104
104
  domain?: string | undefined;
@@ -112,12 +112,12 @@ declare const MainFactsSchema: z.ZodObject<{
112
112
  name: string;
113
113
  relationship_stage: "prospect" | "active" | "churned" | "paused";
114
114
  created?: unknown;
115
+ currency?: string | undefined;
115
116
  domain?: string | undefined;
116
117
  email?: string | undefined;
117
118
  phone?: string | undefined;
118
119
  industry?: string | undefined;
119
120
  deal_value?: number | undefined;
120
- currency?: string | undefined;
121
121
  primary_contact?: string | undefined;
122
122
  timezone?: string | undefined;
123
123
  tags?: string[] | undefined;
@@ -176,8 +176,8 @@ declare const PipelineDealSchema: z.ZodObject<{
176
176
  notes: z.ZodOptional<z.ZodString>;
177
177
  updated: z.ZodString;
178
178
  }, "strip", z.ZodTypeAny, {
179
- name: string;
180
179
  currency: string;
180
+ name: string;
181
181
  updated: string;
182
182
  stage: "lead" | "qualified" | "proposal" | "negotiation" | "won" | "lost";
183
183
  value?: number | undefined;
@@ -211,20 +211,20 @@ declare const TicketSchema: z.ZodObject<{
211
211
  resolved: z.ZodOptional<z.ZodString>;
212
212
  description: z.ZodOptional<z.ZodString>;
213
213
  }, "strip", z.ZodTypeAny, {
214
- status: "open" | "in-progress" | "waiting" | "resolved" | "closed";
215
- created: string;
216
214
  id: string;
217
215
  title: string;
216
+ status: "open" | "in-progress" | "waiting" | "resolved" | "closed";
218
217
  priority: "urgent" | "high" | "normal" | "low";
218
+ created: string;
219
219
  resolved?: string | undefined;
220
220
  assignee?: string | undefined;
221
221
  slaDue?: string | undefined;
222
222
  description?: string | undefined;
223
223
  }, {
224
- status: "open" | "in-progress" | "waiting" | "resolved" | "closed";
225
- created: string;
226
224
  id: string;
227
225
  title: string;
226
+ status: "open" | "in-progress" | "waiting" | "resolved" | "closed";
227
+ created: string;
228
228
  resolved?: string | undefined;
229
229
  priority?: "urgent" | "high" | "normal" | "low" | undefined;
230
230
  assignee?: string | undefined;
@@ -287,7 +287,6 @@ declare const QuoteSchema: z.ZodObject<{
287
287
  htmlPath: z.ZodOptional<z.ZodString>;
288
288
  }, "strip", z.ZodTypeAny, {
289
289
  status: "draft" | "sent" | "viewed" | "accepted" | "declined";
290
- currency: string;
291
290
  total: number;
292
291
  quoteNumber: string;
293
292
  slug: string;
@@ -301,6 +300,7 @@ declare const QuoteSchema: z.ZodObject<{
301
300
  subtotal: number;
302
301
  vatPercent: number;
303
302
  vat: number;
303
+ currency: string;
304
304
  createdAt: string;
305
305
  validUntilDays: number;
306
306
  validUntil: string;
@@ -345,10 +345,10 @@ declare const KbArticleSchema: z.ZodObject<{
345
345
  updatedAt: z.ZodString;
346
346
  sourceTicketId: z.ZodOptional<z.ZodString>;
347
347
  }, "strip", z.ZodTypeAny, {
348
- tags: string[];
349
348
  id: string;
350
349
  title: string;
351
350
  createdAt: string;
351
+ tags: string[];
352
352
  category: string;
353
353
  public: boolean;
354
354
  updatedAt: string;
@@ -388,8 +388,8 @@ declare const SurveyDefinitionSchema: z.ZodObject<{
388
388
  commentPrompt: z.ZodOptional<z.ZodString>;
389
389
  createdAt: z.ZodString;
390
390
  }, "strip", z.ZodTypeAny, {
391
- type: "nps" | "csat" | "ces";
392
391
  id: string;
392
+ type: "nps" | "csat" | "ces";
393
393
  createdAt: string;
394
394
  question: string;
395
395
  scale: {
@@ -543,4 +543,4 @@ declare const VERSION = "0.1.0";
543
543
 
544
544
  //#endregion
545
545
  export { type GlobalSources, type InteractionEntry, type KbArticle, type MainFacts, type PipelineDeal, type QuoteLineItem, type Quote as QuoteRecord, type SurveyDefinition, type SurveyResponse, type TicketPriority, type Ticket as TicketRecord, type TicketStatus, VERSION, canSeeCustomer, clearSession, createCustomer, customerExists, filterAuditLog, getRbacConfig, getRole, getSession, readAuditLog, readMainFacts, runAudit, runBackup, runValidate, setSession };
546
- //# sourceMappingURL=index-DNHsURo5.d.cts.map
546
+ //# sourceMappingURL=index-Ewy4f1XW.d.cts.map