@diskd-ai/email-mcp 0.3.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 (295) hide show
  1. package/LICENSE +155 -0
  2. package/README.md +820 -0
  3. package/dist/cli/account-commands.d.ts +10 -0
  4. package/dist/cli/account-commands.d.ts.map +1 -0
  5. package/dist/cli/account-commands.js +703 -0
  6. package/dist/cli/account-commands.js.map +1 -0
  7. package/dist/cli/config-commands.d.ts +9 -0
  8. package/dist/cli/config-commands.d.ts.map +1 -0
  9. package/dist/cli/config-commands.js +156 -0
  10. package/dist/cli/config-commands.js.map +1 -0
  11. package/dist/cli/guard.d.ts +11 -0
  12. package/dist/cli/guard.d.ts.map +1 -0
  13. package/dist/cli/guard.js +18 -0
  14. package/dist/cli/guard.js.map +1 -0
  15. package/dist/cli/install-commands.d.ts +12 -0
  16. package/dist/cli/install-commands.d.ts.map +1 -0
  17. package/dist/cli/install-commands.js +320 -0
  18. package/dist/cli/install-commands.js.map +1 -0
  19. package/dist/cli/notify.d.ts +8 -0
  20. package/dist/cli/notify.d.ts.map +1 -0
  21. package/dist/cli/notify.js +143 -0
  22. package/dist/cli/notify.js.map +1 -0
  23. package/dist/cli/providers.d.ts +13 -0
  24. package/dist/cli/providers.d.ts.map +1 -0
  25. package/dist/cli/providers.js +180 -0
  26. package/dist/cli/providers.js.map +1 -0
  27. package/dist/cli/scheduler.d.ts +8 -0
  28. package/dist/cli/scheduler.d.ts.map +1 -0
  29. package/dist/cli/scheduler.js +268 -0
  30. package/dist/cli/scheduler.js.map +1 -0
  31. package/dist/cli/setup.d.ts +12 -0
  32. package/dist/cli/setup.d.ts.map +1 -0
  33. package/dist/cli/setup.js +15 -0
  34. package/dist/cli/setup.js.map +1 -0
  35. package/dist/cli/test.d.ts +7 -0
  36. package/dist/cli/test.d.ts.map +1 -0
  37. package/dist/cli/test.js +67 -0
  38. package/dist/cli/test.js.map +1 -0
  39. package/dist/config/loader.d.ts +34 -0
  40. package/dist/config/loader.d.ts.map +1 -0
  41. package/dist/config/loader.js +339 -0
  42. package/dist/config/loader.js.map +1 -0
  43. package/dist/config/schema.d.ts +351 -0
  44. package/dist/config/schema.d.ts.map +1 -0
  45. package/dist/config/schema.js +165 -0
  46. package/dist/config/schema.js.map +1 -0
  47. package/dist/config/xdg.d.ts +27 -0
  48. package/dist/config/xdg.d.ts.map +1 -0
  49. package/dist/config/xdg.js +30 -0
  50. package/dist/config/xdg.js.map +1 -0
  51. package/dist/connections/manager.d.ts +42 -0
  52. package/dist/connections/manager.d.ts.map +1 -0
  53. package/dist/connections/manager.js +266 -0
  54. package/dist/connections/manager.js.map +1 -0
  55. package/dist/connections/types.d.ts +13 -0
  56. package/dist/connections/types.d.ts.map +1 -0
  57. package/dist/connections/types.js +2 -0
  58. package/dist/connections/types.js.map +1 -0
  59. package/dist/logging.d.ts +46 -0
  60. package/dist/logging.d.ts.map +1 -0
  61. package/dist/logging.js +63 -0
  62. package/dist/logging.js.map +1 -0
  63. package/dist/main.d.ts +13 -0
  64. package/dist/main.d.ts.map +1 -0
  65. package/dist/main.js +206 -0
  66. package/dist/main.js.map +1 -0
  67. package/dist/prompts/actions.prompt.d.ts +9 -0
  68. package/dist/prompts/actions.prompt.d.ts.map +1 -0
  69. package/dist/prompts/actions.prompt.js +64 -0
  70. package/dist/prompts/actions.prompt.js.map +1 -0
  71. package/dist/prompts/calendar.prompt.d.ts +9 -0
  72. package/dist/prompts/calendar.prompt.d.ts.map +1 -0
  73. package/dist/prompts/calendar.prompt.js +55 -0
  74. package/dist/prompts/calendar.prompt.js.map +1 -0
  75. package/dist/prompts/cleanup.prompt.d.ts +9 -0
  76. package/dist/prompts/cleanup.prompt.d.ts.map +1 -0
  77. package/dist/prompts/cleanup.prompt.js +78 -0
  78. package/dist/prompts/cleanup.prompt.js.map +1 -0
  79. package/dist/prompts/compose.prompt.d.ts +8 -0
  80. package/dist/prompts/compose.prompt.d.ts.map +1 -0
  81. package/dist/prompts/compose.prompt.js +116 -0
  82. package/dist/prompts/compose.prompt.js.map +1 -0
  83. package/dist/prompts/register.d.ts +8 -0
  84. package/dist/prompts/register.d.ts.map +1 -0
  85. package/dist/prompts/register.js +20 -0
  86. package/dist/prompts/register.js.map +1 -0
  87. package/dist/prompts/thread.prompt.d.ts +9 -0
  88. package/dist/prompts/thread.prompt.d.ts.map +1 -0
  89. package/dist/prompts/thread.prompt.js +58 -0
  90. package/dist/prompts/thread.prompt.js.map +1 -0
  91. package/dist/prompts/triage.prompt.d.ts +9 -0
  92. package/dist/prompts/triage.prompt.d.ts.map +1 -0
  93. package/dist/prompts/triage.prompt.js +64 -0
  94. package/dist/prompts/triage.prompt.js.map +1 -0
  95. package/dist/resources/accounts.resource.d.ts +9 -0
  96. package/dist/resources/accounts.resource.d.ts.map +1 -0
  97. package/dist/resources/accounts.resource.js +26 -0
  98. package/dist/resources/accounts.resource.js.map +1 -0
  99. package/dist/resources/mailboxes.resource.d.ts +10 -0
  100. package/dist/resources/mailboxes.resource.d.ts.map +1 -0
  101. package/dist/resources/mailboxes.resource.js +33 -0
  102. package/dist/resources/mailboxes.resource.js.map +1 -0
  103. package/dist/resources/register.d.ts +12 -0
  104. package/dist/resources/register.d.ts.map +1 -0
  105. package/dist/resources/register.js +20 -0
  106. package/dist/resources/register.js.map +1 -0
  107. package/dist/resources/scheduled.resource.d.ts +9 -0
  108. package/dist/resources/scheduled.resource.d.ts.map +1 -0
  109. package/dist/resources/scheduled.resource.js +31 -0
  110. package/dist/resources/scheduled.resource.js.map +1 -0
  111. package/dist/resources/stats.resource.d.ts +10 -0
  112. package/dist/resources/stats.resource.d.ts.map +1 -0
  113. package/dist/resources/stats.resource.js +45 -0
  114. package/dist/resources/stats.resource.js.map +1 -0
  115. package/dist/resources/templates.resource.d.ts +9 -0
  116. package/dist/resources/templates.resource.d.ts.map +1 -0
  117. package/dist/resources/templates.resource.js +34 -0
  118. package/dist/resources/templates.resource.js.map +1 -0
  119. package/dist/resources/unread.resource.d.ts +11 -0
  120. package/dist/resources/unread.resource.d.ts.map +1 -0
  121. package/dist/resources/unread.resource.js +46 -0
  122. package/dist/resources/unread.resource.js.map +1 -0
  123. package/dist/safety/audit.d.ts +17 -0
  124. package/dist/safety/audit.d.ts.map +1 -0
  125. package/dist/safety/audit.js +50 -0
  126. package/dist/safety/audit.js.map +1 -0
  127. package/dist/safety/rate-limiter.d.ts +22 -0
  128. package/dist/safety/rate-limiter.d.ts.map +1 -0
  129. package/dist/safety/rate-limiter.js +52 -0
  130. package/dist/safety/rate-limiter.js.map +1 -0
  131. package/dist/safety/validation.d.ts +44 -0
  132. package/dist/safety/validation.d.ts.map +1 -0
  133. package/dist/safety/validation.js +113 -0
  134. package/dist/safety/validation.js.map +1 -0
  135. package/dist/server.d.ts +10 -0
  136. package/dist/server.d.ts.map +1 -0
  137. package/dist/server.js +25 -0
  138. package/dist/server.js.map +1 -0
  139. package/dist/services/calendar.service.d.ts +22 -0
  140. package/dist/services/calendar.service.d.ts.map +1 -0
  141. package/dist/services/calendar.service.js +115 -0
  142. package/dist/services/calendar.service.js.map +1 -0
  143. package/dist/services/event-bus.d.ts +28 -0
  144. package/dist/services/event-bus.d.ts.map +1 -0
  145. package/dist/services/event-bus.js +16 -0
  146. package/dist/services/event-bus.js.map +1 -0
  147. package/dist/services/hooks.service.d.ts +77 -0
  148. package/dist/services/hooks.service.d.ts.map +1 -0
  149. package/dist/services/hooks.service.js +498 -0
  150. package/dist/services/hooks.service.js.map +1 -0
  151. package/dist/services/imap.service.d.ts +133 -0
  152. package/dist/services/imap.service.d.ts.map +1 -0
  153. package/dist/services/imap.service.js +1393 -0
  154. package/dist/services/imap.service.js.map +1 -0
  155. package/dist/services/label-strategy.d.ts +20 -0
  156. package/dist/services/label-strategy.d.ts.map +1 -0
  157. package/dist/services/label-strategy.js +237 -0
  158. package/dist/services/label-strategy.js.map +1 -0
  159. package/dist/services/local-calendar.service.d.ts +126 -0
  160. package/dist/services/local-calendar.service.d.ts.map +1 -0
  161. package/dist/services/local-calendar.service.js +428 -0
  162. package/dist/services/local-calendar.service.js.map +1 -0
  163. package/dist/services/notifier.service.d.ts +69 -0
  164. package/dist/services/notifier.service.d.ts.map +1 -0
  165. package/dist/services/notifier.service.js +319 -0
  166. package/dist/services/notifier.service.js.map +1 -0
  167. package/dist/services/oauth.service.d.ts +47 -0
  168. package/dist/services/oauth.service.d.ts.map +1 -0
  169. package/dist/services/oauth.service.js +140 -0
  170. package/dist/services/oauth.service.js.map +1 -0
  171. package/dist/services/presets.d.ts +36 -0
  172. package/dist/services/presets.d.ts.map +1 -0
  173. package/dist/services/presets.js +173 -0
  174. package/dist/services/presets.js.map +1 -0
  175. package/dist/services/reminders.service.d.ts +63 -0
  176. package/dist/services/reminders.service.d.ts.map +1 -0
  177. package/dist/services/reminders.service.js +281 -0
  178. package/dist/services/reminders.service.js.map +1 -0
  179. package/dist/services/scheduler.service.d.ts +42 -0
  180. package/dist/services/scheduler.service.d.ts.map +1 -0
  181. package/dist/services/scheduler.service.js +260 -0
  182. package/dist/services/scheduler.service.js.map +1 -0
  183. package/dist/services/smtp.service.d.ts +40 -0
  184. package/dist/services/smtp.service.d.ts.map +1 -0
  185. package/dist/services/smtp.service.js +151 -0
  186. package/dist/services/smtp.service.js.map +1 -0
  187. package/dist/services/template.service.d.ts +33 -0
  188. package/dist/services/template.service.d.ts.map +1 -0
  189. package/dist/services/template.service.js +123 -0
  190. package/dist/services/template.service.js.map +1 -0
  191. package/dist/services/watcher.service.d.ts +36 -0
  192. package/dist/services/watcher.service.d.ts.map +1 -0
  193. package/dist/services/watcher.service.js +241 -0
  194. package/dist/services/watcher.service.js.map +1 -0
  195. package/dist/tools/accounts.tool.d.ts +7 -0
  196. package/dist/tools/accounts.tool.d.ts.map +1 -0
  197. package/dist/tools/accounts.tool.js +29 -0
  198. package/dist/tools/accounts.tool.js.map +1 -0
  199. package/dist/tools/analytics.tool.d.ts +9 -0
  200. package/dist/tools/analytics.tool.d.ts.map +1 -0
  201. package/dist/tools/analytics.tool.js +27 -0
  202. package/dist/tools/analytics.tool.js.map +1 -0
  203. package/dist/tools/attachments.tool.d.ts +7 -0
  204. package/dist/tools/attachments.tool.d.ts.map +1 -0
  205. package/dist/tools/attachments.tool.js +45 -0
  206. package/dist/tools/attachments.tool.js.map +1 -0
  207. package/dist/tools/bulk.tool.d.ts +7 -0
  208. package/dist/tools/bulk.tool.d.ts.map +1 -0
  209. package/dist/tools/bulk.tool.js +75 -0
  210. package/dist/tools/bulk.tool.js.map +1 -0
  211. package/dist/tools/calendar.tool.d.ts +19 -0
  212. package/dist/tools/calendar.tool.d.ts.map +1 -0
  213. package/dist/tools/calendar.tool.js +538 -0
  214. package/dist/tools/calendar.tool.js.map +1 -0
  215. package/dist/tools/contacts.tool.d.ts +7 -0
  216. package/dist/tools/contacts.tool.d.ts.map +1 -0
  217. package/dist/tools/contacts.tool.js +44 -0
  218. package/dist/tools/contacts.tool.js.map +1 -0
  219. package/dist/tools/drafts.tool.d.ts +8 -0
  220. package/dist/tools/drafts.tool.d.ts.map +1 -0
  221. package/dist/tools/drafts.tool.js +92 -0
  222. package/dist/tools/drafts.tool.js.map +1 -0
  223. package/dist/tools/emails.tool.d.ts +7 -0
  224. package/dist/tools/emails.tool.d.ts.map +1 -0
  225. package/dist/tools/emails.tool.js +400 -0
  226. package/dist/tools/emails.tool.js.map +1 -0
  227. package/dist/tools/folders.tool.d.ts +7 -0
  228. package/dist/tools/folders.tool.d.ts.map +1 -0
  229. package/dist/tools/folders.tool.js +111 -0
  230. package/dist/tools/folders.tool.js.map +1 -0
  231. package/dist/tools/health.tool.d.ts +10 -0
  232. package/dist/tools/health.tool.d.ts.map +1 -0
  233. package/dist/tools/health.tool.js +78 -0
  234. package/dist/tools/health.tool.js.map +1 -0
  235. package/dist/tools/label.tool.d.ts +11 -0
  236. package/dist/tools/label.tool.d.ts.map +1 -0
  237. package/dist/tools/label.tool.js +165 -0
  238. package/dist/tools/label.tool.js.map +1 -0
  239. package/dist/tools/locate.tool.d.ts +11 -0
  240. package/dist/tools/locate.tool.d.ts.map +1 -0
  241. package/dist/tools/locate.tool.js +59 -0
  242. package/dist/tools/locate.tool.js.map +1 -0
  243. package/dist/tools/mailboxes.tool.d.ts +7 -0
  244. package/dist/tools/mailboxes.tool.d.ts.map +1 -0
  245. package/dist/tools/mailboxes.tool.js +38 -0
  246. package/dist/tools/mailboxes.tool.js.map +1 -0
  247. package/dist/tools/manage.tool.d.ts +7 -0
  248. package/dist/tools/manage.tool.d.ts.map +1 -0
  249. package/dist/tools/manage.tool.js +125 -0
  250. package/dist/tools/manage.tool.js.map +1 -0
  251. package/dist/tools/register.d.ts +20 -0
  252. package/dist/tools/register.d.ts.map +1 -0
  253. package/dist/tools/register.js +53 -0
  254. package/dist/tools/register.js.map +1 -0
  255. package/dist/tools/scheduler.tool.d.ts +9 -0
  256. package/dist/tools/scheduler.tool.d.ts.map +1 -0
  257. package/dist/tools/scheduler.tool.js +104 -0
  258. package/dist/tools/scheduler.tool.js.map +1 -0
  259. package/dist/tools/send.tool.d.ts +7 -0
  260. package/dist/tools/send.tool.d.ts.map +1 -0
  261. package/dist/tools/send.tool.js +123 -0
  262. package/dist/tools/send.tool.js.map +1 -0
  263. package/dist/tools/templates.tool.d.ts +12 -0
  264. package/dist/tools/templates.tool.d.ts.map +1 -0
  265. package/dist/tools/templates.tool.js +140 -0
  266. package/dist/tools/templates.tool.js.map +1 -0
  267. package/dist/tools/thread.tool.d.ts +10 -0
  268. package/dist/tools/thread.tool.d.ts.map +1 -0
  269. package/dist/tools/thread.tool.js +146 -0
  270. package/dist/tools/thread.tool.js.map +1 -0
  271. package/dist/tools/watcher.tool.d.ts +9 -0
  272. package/dist/tools/watcher.tool.d.ts.map +1 -0
  273. package/dist/tools/watcher.tool.js +282 -0
  274. package/dist/tools/watcher.tool.js.map +1 -0
  275. package/dist/types/index.d.ts +271 -0
  276. package/dist/types/index.d.ts.map +1 -0
  277. package/dist/types/index.js +5 -0
  278. package/dist/types/index.js.map +1 -0
  279. package/dist/utils/calendar-notes.d.ts +31 -0
  280. package/dist/utils/calendar-notes.d.ts.map +1 -0
  281. package/dist/utils/calendar-notes.js +99 -0
  282. package/dist/utils/calendar-notes.js.map +1 -0
  283. package/dist/utils/calendar-state.d.ts +27 -0
  284. package/dist/utils/calendar-state.d.ts.map +1 -0
  285. package/dist/utils/calendar-state.js +85 -0
  286. package/dist/utils/calendar-state.js.map +1 -0
  287. package/dist/utils/conference-details.d.ts +12 -0
  288. package/dist/utils/conference-details.d.ts.map +1 -0
  289. package/dist/utils/conference-details.js +71 -0
  290. package/dist/utils/conference-details.js.map +1 -0
  291. package/dist/utils/meeting-url.d.ts +10 -0
  292. package/dist/utils/meeting-url.d.ts.map +1 -0
  293. package/dist/utils/meeting-url.js +30 -0
  294. package/dist/utils/meeting-url.js.map +1 -0
  295. package/package.json +108 -0
@@ -0,0 +1,319 @@
1
+ /**
2
+ * NotifierService — multi-channel notification dispatcher.
3
+ *
4
+ * Routes email alerts to the appropriate channels based on urgency:
5
+ * - Desktop notifications (native OS commands — zero npm deps)
6
+ * - Sound alerts (via OS notification sound)
7
+ * - MCP log level escalation (urgent→alert, high→warning, …)
8
+ * - Webhook dispatch (HTTP POST to Slack/Discord/ntfy.sh/etc.)
9
+ *
10
+ * All channels are opt-in and disabled by default.
11
+ */
12
+ import { execFile } from 'node:child_process';
13
+ import { mcpLog } from '../logging.js';
14
+ import { validateWebhookUrl } from '../safety/validation.js';
15
+ // ---------------------------------------------------------------------------
16
+ // Priority ordering for threshold comparison
17
+ // ---------------------------------------------------------------------------
18
+ const URGENCY_ORDER = {
19
+ urgent: 4,
20
+ high: 3,
21
+ normal: 2,
22
+ low: 1,
23
+ };
24
+ const MCP_LOG_LEVEL_MAP = {
25
+ urgent: 'alert',
26
+ high: 'warning',
27
+ normal: 'info',
28
+ low: 'debug',
29
+ };
30
+ // ---------------------------------------------------------------------------
31
+ // Text sanitization — prevent command injection in OS notifications
32
+ // ---------------------------------------------------------------------------
33
+ function sanitizeForShell(text) {
34
+ return text
35
+ .replace(/[\\"'`$]/g, '')
36
+ .replace(/[\n\r\t]/g, ' ')
37
+ .replace(/[^\x20-\x7E\u00A0-\uFFFF]/g, '')
38
+ .slice(0, 200);
39
+ }
40
+ // ---------------------------------------------------------------------------
41
+ // NotifierService
42
+ // ---------------------------------------------------------------------------
43
+ export default class NotifierService {
44
+ config;
45
+ desktopCount = 0;
46
+ desktopResetTimer = null;
47
+ static MAX_DESKTOP_PER_MIN = 5;
48
+ constructor(config) {
49
+ this.config = config;
50
+ // Reset desktop rate counter every 60s
51
+ this.desktopResetTimer = setInterval(() => {
52
+ this.desktopCount = 0;
53
+ }, 60_000);
54
+ }
55
+ stop() {
56
+ if (this.desktopResetTimer) {
57
+ clearInterval(this.desktopResetTimer);
58
+ this.desktopResetTimer = null;
59
+ }
60
+ }
61
+ /** Returns the current alerts configuration. */
62
+ getConfig() {
63
+ return { ...this.config };
64
+ }
65
+ /** Updates alert configuration at runtime (partial merge). */
66
+ updateConfig(partial) {
67
+ this.config = { ...this.config, ...partial };
68
+ return this.getConfig();
69
+ }
70
+ // -------------------------------------------------------------------------
71
+ // Platform diagnostics — check if notification tools are available
72
+ // -------------------------------------------------------------------------
73
+ static async checkPlatformSupport() {
74
+ const { platform } = process;
75
+ const issues = [];
76
+ const instructions = [];
77
+ if (platform === 'darwin') {
78
+ const osascriptOk = await NotifierService.commandExists('osascript');
79
+ const afplayOk = await NotifierService.commandExists('afplay');
80
+ if (!osascriptOk)
81
+ issues.push('osascript not found (should be built-in on macOS)');
82
+ instructions.push('1. Open System Settings → Notifications & Focus', '2. Find your terminal app (Terminal, iTerm2, VS Code, Cursor, etc.)', '3. Enable "Allow Notifications" and choose "Banners" or "Alerts"', '4. Ensure "Do Not Disturb" / Focus mode is not active', '5. If using an MCP client, the notification appears from the terminal running the server');
83
+ return {
84
+ platform: 'macOS',
85
+ supported: osascriptOk,
86
+ desktopTool: { name: 'osascript', available: osascriptOk },
87
+ soundTool: { name: 'afplay', available: afplayOk },
88
+ issues,
89
+ setupInstructions: instructions,
90
+ };
91
+ }
92
+ if (platform === 'linux') {
93
+ const notifySendOk = await NotifierService.commandExists('notify-send');
94
+ const paplayOk = await NotifierService.commandExists('paplay');
95
+ if (!notifySendOk) {
96
+ issues.push('notify-send not found');
97
+ instructions.push('Install libnotify:', ' Ubuntu/Debian: sudo apt install libnotify-bin', ' Fedora: sudo dnf install libnotify', ' Arch: sudo pacman -S libnotify');
98
+ }
99
+ if (!paplayOk) {
100
+ issues.push('paplay not found (needed for sound alerts)');
101
+ instructions.push('Install PulseAudio utils for sound:', ' Ubuntu/Debian: sudo apt install pulseaudio-utils', ' Fedora: sudo dnf install pulseaudio-utils');
102
+ }
103
+ instructions.push('Note: Desktop notifications require a running display server (X11/Wayland).', 'They will not work in headless/SSH sessions.');
104
+ return {
105
+ platform: 'Linux',
106
+ supported: notifySendOk,
107
+ desktopTool: { name: 'notify-send', available: notifySendOk },
108
+ soundTool: { name: 'paplay', available: paplayOk },
109
+ issues,
110
+ setupInstructions: instructions,
111
+ };
112
+ }
113
+ if (platform === 'win32') {
114
+ const psOk = await NotifierService.commandExists('powershell');
115
+ if (!psOk)
116
+ issues.push('PowerShell not found');
117
+ instructions.push('1. Open Settings → System → Notifications', '2. Ensure "Notifications" is turned on', '3. Ensure "Focus Assist" is set to allow notifications', '4. If using Windows Terminal, ensure its notifications are enabled');
118
+ return {
119
+ platform: 'Windows',
120
+ supported: psOk,
121
+ desktopTool: { name: 'powershell', available: psOk },
122
+ soundTool: { name: 'powershell', available: psOk },
123
+ issues,
124
+ setupInstructions: instructions,
125
+ };
126
+ }
127
+ return {
128
+ platform,
129
+ supported: false,
130
+ desktopTool: { name: 'unknown', available: false },
131
+ soundTool: { name: 'unknown', available: false },
132
+ issues: [`Unsupported platform: ${platform}. Desktop notifications are not available.`],
133
+ setupInstructions: ['Desktop notifications are only supported on macOS, Linux, and Windows.'],
134
+ };
135
+ }
136
+ /** Test if a command-line tool exists on the system. */
137
+ static async commandExists(cmd) {
138
+ const bin = process.platform === 'win32' ? 'where' : 'which';
139
+ return new Promise((resolve) => {
140
+ execFile(bin, [cmd], { timeout: 3000 }, (err) => {
141
+ resolve(!err);
142
+ });
143
+ });
144
+ }
145
+ /** Send a test notification to verify platform setup. */
146
+ async sendTestNotification(withSound = false) {
147
+ const diag = await NotifierService.checkPlatformSupport();
148
+ if (!diag.supported) {
149
+ return {
150
+ success: false,
151
+ message: `Desktop notifications not supported: ${diag.issues.join('; ')}`,
152
+ };
153
+ }
154
+ // Temporarily force desktop + sound for the test
155
+ const origDesktop = this.config.desktop;
156
+ const origSound = this.config.sound;
157
+ this.config.desktop = true;
158
+ this.config.sound = withSound;
159
+ try {
160
+ const testPayload = {
161
+ account: 'test',
162
+ sender: { name: 'Email MCP', address: 'test@email-mcp' },
163
+ subject: 'If you see this, notifications work!',
164
+ priority: 'urgent',
165
+ };
166
+ await this.sendDesktopNotification(testPayload);
167
+ return {
168
+ success: true,
169
+ message: withSound
170
+ ? 'Test notification sent with sound. Check your notification center.'
171
+ : 'Test notification sent. Check your notification center.',
172
+ };
173
+ }
174
+ catch (err) {
175
+ const errMsg = err instanceof Error ? err.message : String(err);
176
+ return {
177
+ success: false,
178
+ message: `Notification failed: ${errMsg}. Check platform setup instructions.`,
179
+ };
180
+ }
181
+ finally {
182
+ this.config.desktop = origDesktop;
183
+ this.config.sound = origSound;
184
+ }
185
+ }
186
+ // -------------------------------------------------------------------------
187
+ // Main dispatch — routes alert to channels based on urgency + config
188
+ // -------------------------------------------------------------------------
189
+ async alert(payload, forceDesktop = false) {
190
+ const meetsThreshold = URGENCY_ORDER[payload.priority] >= URGENCY_ORDER[this.config.urgencyThreshold];
191
+ // 1. MCP log — always, with appropriate level
192
+ const logLevel = MCP_LOG_LEVEL_MAP[payload.priority];
193
+ const icon = payload.priority === 'urgent' ? '🚨' : '📧';
194
+ const logMsg = `${icon} [${payload.priority.toUpperCase()}] ${payload.sender.name ?? payload.sender.address}: "${payload.subject}"${payload.labels?.length ? ` [${payload.labels.join(', ')}]` : ''}${payload.ruleName ? ` (rule: ${payload.ruleName})` : ''}`;
195
+ await mcpLog(logLevel, 'notifier', logMsg);
196
+ // 2. Desktop notification — if enabled + meets threshold (or forced by rule)
197
+ if (this.config.desktop && (meetsThreshold || forceDesktop)) {
198
+ await this.sendDesktopNotification(payload);
199
+ }
200
+ // 3. Webhook — if configured + meets webhook event filter
201
+ if (this.config.webhookUrl && this.config.webhookEvents.includes(payload.priority)) {
202
+ this.sendWebhook(payload).catch(() => { });
203
+ }
204
+ }
205
+ // -------------------------------------------------------------------------
206
+ // Desktop notification — native OS commands, zero npm deps
207
+ // -------------------------------------------------------------------------
208
+ async sendDesktopNotification(payload) {
209
+ if (this.desktopCount >= NotifierService.MAX_DESKTOP_PER_MIN)
210
+ return;
211
+ this.desktopCount += 1;
212
+ const title = sanitizeForShell(`📧 Email MCP — ${payload.priority === 'urgent' ? 'Urgent' : 'Important'}`);
213
+ const senderDisplay = sanitizeForShell(payload.sender.name ?? payload.sender.address);
214
+ const subject = sanitizeForShell(payload.subject);
215
+ const body = `From: ${senderDisplay}\n${subject}`;
216
+ const playSound = this.config.sound && payload.priority === 'urgent';
217
+ const { platform } = process;
218
+ try {
219
+ if (platform === 'darwin') {
220
+ await NotifierService.execDarwin(title, body, playSound);
221
+ }
222
+ else if (platform === 'linux') {
223
+ await NotifierService.execLinux(title, body, playSound);
224
+ }
225
+ else if (platform === 'win32') {
226
+ await NotifierService.execWindows(title, body);
227
+ }
228
+ }
229
+ catch {
230
+ // Desktop notification failure is non-fatal — silently degrade to MCP log only
231
+ }
232
+ }
233
+ static async execDarwin(title, body, sound) {
234
+ const soundClause = sound ? ' sound name "Glass"' : '';
235
+ const script = `display notification "${body}" with title "${title}"${soundClause}`;
236
+ await NotifierService.execCommand('osascript', ['-e', script]);
237
+ }
238
+ static async execLinux(title, body, sound) {
239
+ const urgency = sound ? 'critical' : 'normal';
240
+ await NotifierService.execCommand('notify-send', ['-u', urgency, title, body]);
241
+ if (sound) {
242
+ try {
243
+ await NotifierService.execCommand('paplay', [
244
+ '/usr/share/sounds/freedesktop/stereo/message-new-instant.oga',
245
+ ]);
246
+ }
247
+ catch {
248
+ // Sound playback failure is non-fatal
249
+ }
250
+ }
251
+ }
252
+ static async execWindows(title, body) {
253
+ const ps = `[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); ` +
254
+ `$n = New-Object System.Windows.Forms.NotifyIcon; ` +
255
+ `$n.Icon = [System.Drawing.SystemIcons]::Information; ` +
256
+ `$n.Visible = $true; ` +
257
+ `$n.ShowBalloonTip(5000, '${title}', '${body}', 'Info')`;
258
+ await NotifierService.execCommand('powershell', ['-Command', ps]);
259
+ }
260
+ static async execCommand(bin, args) {
261
+ return new Promise((resolve, reject) => {
262
+ execFile(bin, args, { timeout: 5000 }, (err) => {
263
+ if (err)
264
+ reject(err);
265
+ else
266
+ resolve();
267
+ });
268
+ });
269
+ }
270
+ // -------------------------------------------------------------------------
271
+ // Webhook dispatch — HTTP POST with JSON payload
272
+ // -------------------------------------------------------------------------
273
+ async sendWebhook(payload) {
274
+ if (!this.config.webhookUrl)
275
+ return;
276
+ try {
277
+ validateWebhookUrl(this.config.webhookUrl);
278
+ }
279
+ catch (err) {
280
+ await mcpLog('warning', 'notifier', `Invalid webhook URL: ${err instanceof Error ? err.message : String(err)}`);
281
+ return;
282
+ }
283
+ const body = JSON.stringify({
284
+ event: `email.${payload.priority}`,
285
+ account: payload.account,
286
+ uid: payload.uid ?? null,
287
+ folder: payload.folder ?? null,
288
+ sender: payload.sender,
289
+ subject: payload.subject,
290
+ hasAttachments: payload.hasAttachments ?? false,
291
+ priority: payload.priority,
292
+ labels: payload.labels ?? [],
293
+ rule: payload.ruleName ?? null,
294
+ timestamp: new Date().toISOString(),
295
+ });
296
+ const controller = new AbortController();
297
+ const timeout = setTimeout(() => {
298
+ controller.abort();
299
+ }, 5000);
300
+ try {
301
+ const resp = await fetch(this.config.webhookUrl, {
302
+ method: 'POST',
303
+ headers: { 'Content-Type': 'application/json' },
304
+ body,
305
+ signal: controller.signal,
306
+ });
307
+ if (!resp.ok) {
308
+ await mcpLog('warning', 'notifier', `Webhook returned ${resp.status}`);
309
+ }
310
+ }
311
+ catch {
312
+ await mcpLog('debug', 'notifier', 'Webhook dispatch failed (non-fatal)');
313
+ }
314
+ finally {
315
+ clearTimeout(timeout);
316
+ }
317
+ }
318
+ }
319
+ //# sourceMappingURL=notifier.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notifier.service.js","sourceRoot":"","sources":["../../src/services/notifier.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * OAuth2 token management service.
3
+ *
4
+ * Handles token refresh and caching for Google and Microsoft OAuth2/XOAUTH2.
5
+ * Uses native fetch (Node 22+) — no external HTTP dependencies.
6
+ */
7
+ import type { OAuth2Config } from '../types/index.js';
8
+ interface ProviderEndpoints {
9
+ tokenUrl: string;
10
+ authUrl: string;
11
+ scopes: string[];
12
+ }
13
+ export default class OAuthService {
14
+ /**
15
+ * Get a valid access token, refreshing if expired.
16
+ */
17
+ getAccessToken(oauth2: OAuth2Config): Promise<string>;
18
+ /**
19
+ * Check if the cached access token is expired or missing.
20
+ */
21
+ static isTokenExpired(oauth2: OAuth2Config): boolean;
22
+ /**
23
+ * Exchange refresh token for a new access token.
24
+ */
25
+ static refreshAccessToken(oauth2: OAuth2Config): Promise<{
26
+ accessToken: string;
27
+ expiresIn: number;
28
+ }>;
29
+ /**
30
+ * Get provider endpoints for token exchange and authorization.
31
+ */
32
+ static getProviderEndpoints(oauth2: OAuth2Config): ProviderEndpoints;
33
+ /**
34
+ * Generate an OAuth2 authorization URL for the CLI setup wizard.
35
+ */
36
+ static generateAuthUrl(oauth2: OAuth2Config, redirectUri: string): string;
37
+ /**
38
+ * Exchange an authorization code for tokens (initial setup).
39
+ */
40
+ static exchangeCode(oauth2: OAuth2Config, code: string, redirectUri: string): Promise<{
41
+ accessToken: string;
42
+ refreshToken: string;
43
+ expiresIn: number;
44
+ }>;
45
+ }
46
+ export {};
47
+ //# sourceMappingURL=oauth.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.service.d.ts","sourceRoot":"","sources":["../../src/services/oauth.service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAMtD,UAAU,iBAAiB;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAsBD,MAAM,CAAC,OAAO,OAAO,YAAY;IAC/B;;OAEG;IAEG,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAgB3D;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO;IAKpD;;OAEG;WACU,kBAAkB,CAC7B,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAgCtD;;OAEG;IACH,MAAM,CAAC,oBAAoB,CAAC,MAAM,EAAE,YAAY,GAAG,iBAAiB;IAmBpE;;OAEG;IACH,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM;IAazE;;OAEG;WACU,YAAY,CACvB,MAAM,EAAE,YAAY,EACpB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;CAkC7E"}
@@ -0,0 +1,140 @@
1
+ /**
2
+ * OAuth2 token management service.
3
+ *
4
+ * Handles token refresh and caching for Google and Microsoft OAuth2/XOAUTH2.
5
+ * Uses native fetch (Node 22+) — no external HTTP dependencies.
6
+ */
7
+ const PROVIDER_ENDPOINTS = {
8
+ google: {
9
+ tokenUrl: 'https://oauth2.googleapis.com/token',
10
+ authUrl: 'https://accounts.google.com/o/oauth2/v2/auth',
11
+ scopes: ['https://mail.google.com/'],
12
+ },
13
+ microsoft: {
14
+ tokenUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
15
+ authUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
16
+ scopes: [
17
+ 'https://outlook.office.com/IMAP.AccessAsUser.All',
18
+ 'https://outlook.office.com/SMTP.Send',
19
+ 'offline_access',
20
+ ],
21
+ },
22
+ };
23
+ /** 5-minute safety buffer before token expiry */
24
+ const TOKEN_EXPIRY_BUFFER_MS = 5 * 60 * 1000;
25
+ export default class OAuthService {
26
+ /**
27
+ * Get a valid access token, refreshing if expired.
28
+ */
29
+ // eslint-disable-next-line class-methods-use-this
30
+ async getAccessToken(oauth2) {
31
+ if (!OAuthService.isTokenExpired(oauth2) && oauth2.accessToken) {
32
+ return oauth2.accessToken;
33
+ }
34
+ const result = await OAuthService.refreshAccessToken(oauth2);
35
+ // Cache on the config object (in-memory only)
36
+ Object.assign(oauth2, {
37
+ accessToken: result.accessToken,
38
+ tokenExpiry: Date.now() + result.expiresIn * 1000,
39
+ });
40
+ return result.accessToken;
41
+ }
42
+ /**
43
+ * Check if the cached access token is expired or missing.
44
+ */
45
+ static isTokenExpired(oauth2) {
46
+ if (!oauth2.accessToken || !oauth2.tokenExpiry)
47
+ return true;
48
+ return Date.now() >= oauth2.tokenExpiry - TOKEN_EXPIRY_BUFFER_MS;
49
+ }
50
+ /**
51
+ * Exchange refresh token for a new access token.
52
+ */
53
+ static async refreshAccessToken(oauth2) {
54
+ const endpoints = OAuthService.getProviderEndpoints(oauth2);
55
+ const body = new URLSearchParams({
56
+ grant_type: 'refresh_token',
57
+ client_id: oauth2.clientId,
58
+ client_secret: oauth2.clientSecret,
59
+ refresh_token: oauth2.refreshToken,
60
+ });
61
+ const response = await fetch(endpoints.tokenUrl, {
62
+ method: 'POST',
63
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
64
+ body: body.toString(),
65
+ });
66
+ if (!response.ok) {
67
+ const text = await response.text();
68
+ throw new Error(`OAuth2 token refresh failed (${response.status}): ${text}`);
69
+ }
70
+ const data = (await response.json());
71
+ return {
72
+ accessToken: data.access_token,
73
+ expiresIn: data.expires_in,
74
+ };
75
+ }
76
+ /**
77
+ * Get provider endpoints for token exchange and authorization.
78
+ */
79
+ static getProviderEndpoints(oauth2) {
80
+ if (oauth2.provider === 'custom') {
81
+ if (!oauth2.tokenUrl || !oauth2.authUrl) {
82
+ throw new Error('Custom OAuth2 provider requires tokenUrl and authUrl');
83
+ }
84
+ return {
85
+ tokenUrl: oauth2.tokenUrl,
86
+ authUrl: oauth2.authUrl,
87
+ scopes: oauth2.scopes ?? [],
88
+ };
89
+ }
90
+ const endpoints = PROVIDER_ENDPOINTS[oauth2.provider];
91
+ if (!endpoints) {
92
+ throw new Error(`Unknown OAuth2 provider: ${oauth2.provider}`);
93
+ }
94
+ return endpoints;
95
+ }
96
+ /**
97
+ * Generate an OAuth2 authorization URL for the CLI setup wizard.
98
+ */
99
+ static generateAuthUrl(oauth2, redirectUri) {
100
+ const endpoints = OAuthService.getProviderEndpoints(oauth2);
101
+ const params = new URLSearchParams({
102
+ client_id: oauth2.clientId,
103
+ redirect_uri: redirectUri,
104
+ response_type: 'code',
105
+ scope: endpoints.scopes.join(' '),
106
+ access_type: 'offline',
107
+ prompt: 'consent',
108
+ });
109
+ return `${endpoints.authUrl}?${params.toString()}`;
110
+ }
111
+ /**
112
+ * Exchange an authorization code for tokens (initial setup).
113
+ */
114
+ static async exchangeCode(oauth2, code, redirectUri) {
115
+ const endpoints = OAuthService.getProviderEndpoints(oauth2);
116
+ const body = new URLSearchParams({
117
+ grant_type: 'authorization_code',
118
+ client_id: oauth2.clientId,
119
+ client_secret: oauth2.clientSecret,
120
+ code,
121
+ redirect_uri: redirectUri,
122
+ });
123
+ const response = await fetch(endpoints.tokenUrl, {
124
+ method: 'POST',
125
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
126
+ body: body.toString(),
127
+ });
128
+ if (!response.ok) {
129
+ const text = await response.text();
130
+ throw new Error(`OAuth2 code exchange failed (${response.status}): ${text}`);
131
+ }
132
+ const data = (await response.json());
133
+ return {
134
+ accessToken: data.access_token,
135
+ refreshToken: data.refresh_token,
136
+ expiresIn: data.expires_in,
137
+ };
138
+ }
139
+ }
140
+ //# sourceMappingURL=oauth.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.service.js","sourceRoot":"","sources":["../../src/services/oauth.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Built-in hook presets — ready-to-use AI triage profiles.
3
+ *
4
+ * Each preset provides a system prompt template, suggested labels,
5
+ * and a description. Users select a preset in config.toml and can
6
+ * append their own `custom_instructions` for personalisation.
7
+ */
8
+ export interface HookPreset {
9
+ /** Unique identifier used in config (e.g. "inbox-zero"). */
10
+ id: string;
11
+ /** Human-readable name. */
12
+ name: string;
13
+ /** Short description shown in `list_presets`. */
14
+ description: string;
15
+ /** System prompt template. `{{LABELS}}` and `{{CUSTOM}}` are replaced at runtime. */
16
+ systemPrompt: string;
17
+ /** Labels the AI is encouraged to use when triaging. */
18
+ suggestedLabels: string[];
19
+ }
20
+ export declare const DEFAULT_PRESET_ID = "priority-focus";
21
+ /** Get a preset by id. Returns undefined for unknown ids. */
22
+ export declare function getPreset(id: string): HookPreset | undefined;
23
+ /** List all available presets. */
24
+ export declare function listPresets(): HookPreset[];
25
+ /**
26
+ * Build the final system prompt for AI triage by combining:
27
+ * 1. The preset's system prompt template
28
+ * 2. The preset's suggested labels
29
+ * 3. The user's custom instructions (if any)
30
+ * 4. OR the user's full system_prompt override (if preset = "custom")
31
+ */
32
+ export declare function buildSystemPrompt(presetId: string, options?: {
33
+ customInstructions?: string;
34
+ systemPrompt?: string;
35
+ }): string;
36
+ //# sourceMappingURL=presets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../src/services/presets.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,MAAM,WAAW,UAAU;IACzB,4DAA4D;IAC5D,EAAE,EAAE,MAAM,CAAC;IACX,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,WAAW,EAAE,MAAM,CAAC;IACpB,sFAAsF;IACtF,YAAY,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAyID,eAAO,MAAM,iBAAiB,mBAAmB,CAAC;AAElD,6DAA6D;AAC7D,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAE5D;AAED,kCAAkC;AAClC,wBAAgB,WAAW,IAAI,UAAU,EAAE,CAE1C;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAO,GACnE,MAAM,CAiCR"}