@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,703 @@
1
+ /**
2
+ * Account management subcommands.
3
+ *
4
+ * - account list — list all configured accounts
5
+ * - account add — interactive wizard to add a new account
6
+ * - account edit [name] — edit an existing account interactively
7
+ * - account delete [name] — remove an account with confirmation
8
+ */
9
+ import { cancel, confirm, intro, isCancel, log, note, outro, password as p_password, spinner as p_spinner, select, text, } from '@clack/prompts';
10
+ import { CONFIG_FILE, configExists, loadRawConfig, saveConfig } from '../config/loader.js';
11
+ import { AppConfigFileSchema } from '../config/schema.js';
12
+ import ConnectionManager from '../connections/manager.js';
13
+ import ensureInteractive from './guard.js';
14
+ import { detectProvider } from './providers.js';
15
+ // ---------------------------------------------------------------------------
16
+ // Helpers
17
+ // ---------------------------------------------------------------------------
18
+ class CancelledError extends Error {
19
+ constructor() {
20
+ super('Operation cancelled.');
21
+ }
22
+ }
23
+ function assertNotCancel(value) {
24
+ if (isCancel(value)) {
25
+ cancel('Operation cancelled.');
26
+ throw new CancelledError();
27
+ }
28
+ }
29
+ function formatSecurity(tls, starttls) {
30
+ if (tls)
31
+ return 'TLS';
32
+ if (starttls)
33
+ return 'STARTTLS';
34
+ return 'plain';
35
+ }
36
+ function getSmtpLabel(starttls, tls) {
37
+ if (starttls)
38
+ return 'STARTTLS';
39
+ if (tls)
40
+ return 'TLS';
41
+ return 'plain';
42
+ }
43
+ function resolveSmtpSecurityDefault(defaults) {
44
+ if (defaults?.smtpStarttls)
45
+ return 'starttls';
46
+ if (defaults?.smtpTls ?? true)
47
+ return 'tls';
48
+ return 'none';
49
+ }
50
+ async function promptServerSettings(defaults) {
51
+ const imapHost = await text({
52
+ message: 'IMAP host',
53
+ placeholder: 'imap.example.com',
54
+ defaultValue: defaults?.imapHost,
55
+ initialValue: defaults?.imapHost,
56
+ validate: (v) => (!v || v.length === 0 ? 'IMAP host is required' : undefined),
57
+ });
58
+ assertNotCancel(imapHost);
59
+ const imapPortStr = await text({
60
+ message: 'IMAP port',
61
+ placeholder: '993',
62
+ defaultValue: String(defaults?.imapPort ?? 993),
63
+ initialValue: defaults?.imapPort ? String(defaults.imapPort) : undefined,
64
+ });
65
+ assertNotCancel(imapPortStr);
66
+ const imapTls = await confirm({
67
+ message: 'IMAP use TLS?',
68
+ initialValue: defaults?.imapTls ?? true,
69
+ });
70
+ assertNotCancel(imapTls);
71
+ const smtpHost = await text({
72
+ message: 'SMTP host',
73
+ placeholder: 'smtp.example.com',
74
+ defaultValue: defaults?.smtpHost,
75
+ initialValue: defaults?.smtpHost,
76
+ validate: (v) => (!v || v.length === 0 ? 'SMTP host is required' : undefined),
77
+ });
78
+ assertNotCancel(smtpHost);
79
+ const smtpPortStr = await text({
80
+ message: 'SMTP port',
81
+ placeholder: '465',
82
+ defaultValue: String(defaults?.smtpPort ?? 465),
83
+ initialValue: defaults?.smtpPort ? String(defaults.smtpPort) : undefined,
84
+ });
85
+ assertNotCancel(smtpPortStr);
86
+ const smtpSecurity = await select({
87
+ message: 'SMTP security',
88
+ initialValue: resolveSmtpSecurityDefault(defaults),
89
+ options: [
90
+ { value: 'tls', label: 'TLS (port 465)' },
91
+ { value: 'starttls', label: 'STARTTLS (port 587)' },
92
+ { value: 'none', label: 'None (not recommended)' },
93
+ ],
94
+ });
95
+ assertNotCancel(smtpSecurity);
96
+ const smtpPoolEnabled = await confirm({
97
+ message: 'Enable SMTP connection pooling?',
98
+ initialValue: defaults?.smtpPoolEnabled ?? true,
99
+ });
100
+ assertNotCancel(smtpPoolEnabled);
101
+ let smtpPoolMaxConnections = defaults?.smtpPoolMaxConnections ?? 1;
102
+ let smtpPoolMaxMessages = defaults?.smtpPoolMaxMessages ?? 100;
103
+ if (smtpPoolEnabled) {
104
+ const maxConnectionsStr = await text({
105
+ message: 'SMTP pool max connections',
106
+ defaultValue: String(defaults?.smtpPoolMaxConnections ?? 1),
107
+ initialValue: defaults?.smtpPoolMaxConnections !== undefined
108
+ ? String(defaults.smtpPoolMaxConnections)
109
+ : undefined,
110
+ validate: (v) => {
111
+ if (!v)
112
+ return 'Must be a positive integer';
113
+ const n = parseInt(v, 10);
114
+ if (Number.isNaN(n) || n < 1)
115
+ return 'Must be a positive integer';
116
+ return undefined;
117
+ },
118
+ });
119
+ assertNotCancel(maxConnectionsStr);
120
+ const maxMessagesStr = await text({
121
+ message: 'SMTP pool max messages per connection',
122
+ defaultValue: String(defaults?.smtpPoolMaxMessages ?? 100),
123
+ initialValue: defaults?.smtpPoolMaxMessages !== undefined
124
+ ? String(defaults.smtpPoolMaxMessages)
125
+ : undefined,
126
+ validate: (v) => {
127
+ if (!v)
128
+ return 'Must be a positive integer';
129
+ const n = parseInt(v, 10);
130
+ if (Number.isNaN(n) || n < 1)
131
+ return 'Must be a positive integer';
132
+ return undefined;
133
+ },
134
+ });
135
+ assertNotCancel(maxMessagesStr);
136
+ smtpPoolMaxConnections = parseInt(maxConnectionsStr || '1', 10);
137
+ smtpPoolMaxMessages = parseInt(maxMessagesStr || '100', 10);
138
+ }
139
+ return {
140
+ imapHost,
141
+ imapPort: parseInt(imapPortStr || '993', 10),
142
+ imapTls,
143
+ smtpHost,
144
+ smtpPort: parseInt(smtpPortStr || '465', 10),
145
+ smtpTls: smtpSecurity === 'tls',
146
+ smtpStarttls: smtpSecurity === 'starttls',
147
+ smtpPoolEnabled,
148
+ smtpPoolMaxConnections,
149
+ smtpPoolMaxMessages,
150
+ };
151
+ }
152
+ /**
153
+ * Prompt for account identity (name, email, full name).
154
+ */
155
+ async function promptAccountIdentity(defaults) {
156
+ const name = await text({
157
+ message: 'Account name',
158
+ placeholder: 'personal',
159
+ defaultValue: defaults?.name,
160
+ initialValue: defaults?.name,
161
+ validate: (v) => (!v || v.length === 0 ? 'Account name is required' : undefined),
162
+ });
163
+ assertNotCancel(name);
164
+ const email = await text({
165
+ message: 'Email address',
166
+ placeholder: 'you@example.com',
167
+ defaultValue: defaults?.email,
168
+ initialValue: defaults?.email,
169
+ validate: (v) => (v?.includes('@') ? undefined : 'Please enter a valid email address'),
170
+ });
171
+ assertNotCancel(email);
172
+ const fullName = await text({
173
+ message: 'Full name (optional)',
174
+ placeholder: 'Your Name',
175
+ defaultValue: defaults?.fullName,
176
+ initialValue: defaults?.fullName,
177
+ });
178
+ assertNotCancel(fullName);
179
+ return { name, email, fullName };
180
+ }
181
+ /**
182
+ * Prompt for credentials (username, password).
183
+ */
184
+ async function promptCredentials(defaults) {
185
+ const username = await text({
186
+ message: 'Username',
187
+ placeholder: defaults?.email ?? 'you@example.com',
188
+ defaultValue: defaults?.username ?? defaults?.email,
189
+ initialValue: defaults?.username ?? defaults?.email,
190
+ });
191
+ assertNotCancel(username);
192
+ const password = await p_password({
193
+ message: 'Password / App Password',
194
+ validate: (v) => (!v || v.length === 0 ? 'Password is required' : undefined),
195
+ });
196
+ assertNotCancel(password);
197
+ return { username, password };
198
+ }
199
+ /**
200
+ * Auto-detect provider settings from email domain.
201
+ * Returns server settings or prompts for manual entry.
202
+ */
203
+ async function resolveServerSettings(email, defaults) {
204
+ const provider = detectProvider(email);
205
+ if (provider) {
206
+ log.success(`Auto-detected: ${provider.name}`);
207
+ log.info(` IMAP: ${provider.imap.host}:${provider.imap.port} (${provider.imap.tls ? 'TLS' : 'plain'})`);
208
+ const smtpLabel = getSmtpLabel(provider.smtp.starttls, provider.smtp.tls);
209
+ log.info(` SMTP: ${provider.smtp.host}:${provider.smtp.port} (${smtpLabel})`);
210
+ if (provider.notes) {
211
+ log.warning(` Note: ${provider.notes}`);
212
+ }
213
+ const useDetected = await confirm({
214
+ message: 'Use detected settings?',
215
+ initialValue: true,
216
+ });
217
+ assertNotCancel(useDetected);
218
+ if (useDetected) {
219
+ return {
220
+ imapHost: provider.imap.host,
221
+ imapPort: provider.imap.port,
222
+ imapTls: provider.imap.tls,
223
+ smtpHost: provider.smtp.host,
224
+ smtpPort: provider.smtp.port,
225
+ smtpTls: provider.smtp.tls,
226
+ smtpStarttls: provider.smtp.starttls,
227
+ smtpPoolEnabled: true,
228
+ smtpPoolMaxConnections: 1,
229
+ smtpPoolMaxMessages: 100,
230
+ };
231
+ }
232
+ }
233
+ else {
234
+ log.info('Provider not auto-detected. Please enter server settings manually.');
235
+ }
236
+ return promptServerSettings(defaults);
237
+ }
238
+ /**
239
+ * Build a normalized AccountConfig for connection testing.
240
+ */
241
+ function buildTestAccount(identity, creds, server) {
242
+ return {
243
+ name: identity.name,
244
+ email: identity.email,
245
+ fullName: identity.fullName || undefined,
246
+ username: creds.username || identity.email,
247
+ password: creds.password,
248
+ imap: {
249
+ host: server.imapHost,
250
+ port: server.imapPort,
251
+ tls: server.imapTls,
252
+ starttls: !server.imapTls,
253
+ verifySsl: true,
254
+ },
255
+ smtp: {
256
+ host: server.smtpHost,
257
+ port: server.smtpPort,
258
+ tls: server.smtpTls,
259
+ starttls: server.smtpStarttls,
260
+ verifySsl: true,
261
+ pool: {
262
+ enabled: server.smtpPoolEnabled,
263
+ maxConnections: server.smtpPoolMaxConnections,
264
+ maxMessages: server.smtpPoolMaxMessages,
265
+ },
266
+ },
267
+ };
268
+ }
269
+ /**
270
+ * Test IMAP and SMTP connections. Returns true if both succeed or user opts to proceed.
271
+ */
272
+ async function testConnections(account) {
273
+ const spinner = p_spinner();
274
+ spinner.start('Testing IMAP connection…');
275
+ const imapResult = await ConnectionManager.testImap(account);
276
+ if (imapResult.success) {
277
+ spinner.stop(`IMAP ✓ ${account.imap.host}:${account.imap.port} — ${imapResult.details?.messages} messages, ${imapResult.details?.folders} folders`);
278
+ }
279
+ else {
280
+ spinner.stop(`IMAP ✗ ${imapResult.error}`);
281
+ }
282
+ spinner.start('Testing SMTP connection…');
283
+ const smtpResult = await ConnectionManager.testSmtp(account);
284
+ if (smtpResult.success) {
285
+ spinner.stop(`SMTP ✓ ${account.smtp.host}:${account.smtp.port} — authenticated`);
286
+ }
287
+ else {
288
+ spinner.stop(`SMTP ✗ ${smtpResult.error}`);
289
+ }
290
+ if (!imapResult.success || !smtpResult.success) {
291
+ const proceed = await confirm({
292
+ message: 'Some connections failed. Save config anyway?',
293
+ initialValue: false,
294
+ });
295
+ if (isCancel(proceed) || !proceed) {
296
+ cancel('Cancelled. Please check your credentials and server settings.');
297
+ return false;
298
+ }
299
+ }
300
+ return true;
301
+ }
302
+ /**
303
+ * Build a RawAccountConfig from collected data.
304
+ */
305
+ function buildRawAccount(identity, creds, server) {
306
+ return {
307
+ name: identity.name,
308
+ email: identity.email,
309
+ full_name: identity.fullName || undefined,
310
+ username: creds.username || identity.email,
311
+ password: creds.password,
312
+ imap: {
313
+ host: server.imapHost,
314
+ port: server.imapPort,
315
+ tls: server.imapTls,
316
+ starttls: !server.imapTls,
317
+ verify_ssl: true,
318
+ },
319
+ smtp: {
320
+ host: server.smtpHost,
321
+ port: server.smtpPort,
322
+ tls: server.smtpTls,
323
+ starttls: server.smtpStarttls,
324
+ verify_ssl: true,
325
+ pool: {
326
+ enabled: server.smtpPoolEnabled,
327
+ max_connections: server.smtpPoolMaxConnections,
328
+ max_messages: server.smtpPoolMaxMessages,
329
+ },
330
+ },
331
+ };
332
+ }
333
+ // ---------------------------------------------------------------------------
334
+ // Subcommands
335
+ // ---------------------------------------------------------------------------
336
+ /**
337
+ * List all configured accounts in a formatted table.
338
+ */
339
+ async function listAccounts() {
340
+ const exists = await configExists();
341
+ if (!exists) {
342
+ console.error(`No config file found at: ${CONFIG_FILE}`);
343
+ console.error("Run 'email-mcp account add' to create one.");
344
+ return;
345
+ }
346
+ const config = await loadRawConfig();
347
+ const { accounts } = config;
348
+ if (accounts.length === 0) {
349
+ console.log('No accounts configured.');
350
+ return;
351
+ }
352
+ console.log(`\n ${'Name'.padEnd(16)} ${'Email'.padEnd(30)} ${'IMAP'.padEnd(28)} SMTP`);
353
+ console.log(` ${'─'.repeat(16)} ${'─'.repeat(30)} ${'─'.repeat(28)} ${'─'.repeat(28)}`);
354
+ accounts.forEach((acct) => {
355
+ const imapInfo = `${acct.imap.host}:${acct.imap.port} (${formatSecurity(acct.imap.tls, acct.imap.starttls)})`;
356
+ const smtpInfo = `${acct.smtp.host}:${acct.smtp.port} (${formatSecurity(acct.smtp.tls, acct.smtp.starttls)})`;
357
+ console.log(` ${acct.name.padEnd(16)} ${acct.email.padEnd(30)} ${imapInfo.padEnd(28)} ${smtpInfo}`);
358
+ });
359
+ console.log(`\n ${accounts.length} account(s) configured.\n`);
360
+ }
361
+ /**
362
+ * Interactive wizard to add a new email account.
363
+ */
364
+ async function addAccount() {
365
+ ensureInteractive();
366
+ intro('email-mcp › Add Account');
367
+ let existingConfig;
368
+ const exists = await configExists();
369
+ if (exists) {
370
+ try {
371
+ existingConfig = await loadRawConfig();
372
+ }
373
+ catch {
374
+ log.warning('Could not parse existing config. A new config will be created.');
375
+ }
376
+ }
377
+ // 1. Identity
378
+ const identity = await promptAccountIdentity();
379
+ // Check for duplicate names
380
+ if (existingConfig?.accounts.some((a) => a.name === identity.name)) {
381
+ log.error(`Account "${identity.name}" already exists. Use 'account edit' to modify it.`);
382
+ cancel('Duplicate account name.');
383
+ return;
384
+ }
385
+ // 2. Server settings (auto-detect or manual)
386
+ const server = await resolveServerSettings(identity.email);
387
+ // 3. Credentials
388
+ const creds = await promptCredentials({ email: identity.email });
389
+ // 4. Test connections
390
+ const testAccount = buildTestAccount(identity, creds, server);
391
+ const ok = await testConnections(testAccount);
392
+ if (!ok)
393
+ return;
394
+ // 5. Build and save
395
+ const newAccount = buildRawAccount(identity, creds, server);
396
+ const config = existingConfig
397
+ ? {
398
+ ...existingConfig,
399
+ accounts: [...existingConfig.accounts, newAccount],
400
+ }
401
+ : {
402
+ settings: {
403
+ rate_limit: 10,
404
+ read_only: false,
405
+ watcher: {
406
+ enabled: false,
407
+ folders: ['INBOX'],
408
+ idle_timeout: 1740,
409
+ },
410
+ hooks: {
411
+ on_new_email: 'notify',
412
+ preset: 'priority-focus',
413
+ auto_label: false,
414
+ auto_flag: false,
415
+ batch_delay: 5,
416
+ rules: [],
417
+ alerts: {
418
+ desktop: false,
419
+ sound: false,
420
+ urgency_threshold: 'high',
421
+ webhook_url: '',
422
+ webhook_events: ['urgent', 'high'],
423
+ },
424
+ auto_calendar: false,
425
+ calendar_name: '',
426
+ calendar_alarm_minutes: 15,
427
+ calendar_confirm: true,
428
+ },
429
+ },
430
+ accounts: [newAccount],
431
+ };
432
+ AppConfigFileSchema.parse(config);
433
+ await saveConfig(config);
434
+ log.success(`Account "${identity.name}" added. Config saved to ${CONFIG_FILE}`);
435
+ note(JSON.stringify({
436
+ mcpServers: {
437
+ email: {
438
+ command: 'email-mcp',
439
+ args: ['stdio'],
440
+ },
441
+ },
442
+ }, null, 2), 'Add this to your MCP client config (e.g., Claude Desktop, Cursor)');
443
+ outro('Done!');
444
+ }
445
+ /**
446
+ * Interactive editor for an existing account.
447
+ * Shows a field selector so users can pick exactly what to change.
448
+ */
449
+ async function editAccount(nameArg) {
450
+ ensureInteractive();
451
+ intro('email-mcp › Edit Account');
452
+ const exists = await configExists();
453
+ if (!exists) {
454
+ log.error(`No config file found at: ${CONFIG_FILE}`);
455
+ cancel("Run 'email-mcp account add' first.");
456
+ return;
457
+ }
458
+ const config = await loadRawConfig();
459
+ const { accounts } = config;
460
+ // Resolve which account to edit
461
+ let accountIndex;
462
+ if (nameArg) {
463
+ accountIndex = accounts.findIndex((a) => a.name === nameArg);
464
+ if (accountIndex === -1) {
465
+ log.error(`Account "${nameArg}" not found.`);
466
+ log.info(`Available accounts: ${accounts.map((a) => a.name).join(', ')}`);
467
+ cancel('Account not found.');
468
+ return;
469
+ }
470
+ }
471
+ else if (accounts.length === 1) {
472
+ accountIndex = 0;
473
+ log.info(`Editing account "${accounts[0].name}".`);
474
+ }
475
+ else {
476
+ const chosen = await select({
477
+ message: 'Which account do you want to edit?',
478
+ options: accounts.map((a, i) => ({
479
+ value: i,
480
+ label: a.name,
481
+ hint: a.email,
482
+ })),
483
+ });
484
+ assertNotCancel(chosen);
485
+ accountIndex = chosen;
486
+ }
487
+ const current = accounts[accountIndex];
488
+ // Field selector — let user choose what to edit
489
+ const fields = await select({
490
+ message: `Editing "${current.name}" — what would you like to change?`,
491
+ options: [
492
+ {
493
+ value: 'identity',
494
+ label: 'Name, email, or display name',
495
+ hint: `${current.name} <${current.email}>`,
496
+ },
497
+ {
498
+ value: 'servers',
499
+ label: 'Server settings (IMAP/SMTP)',
500
+ hint: `${current.imap.host}, ${current.smtp.host}`,
501
+ },
502
+ {
503
+ value: 'credentials',
504
+ label: 'Username or password',
505
+ hint: current.username ?? current.email,
506
+ },
507
+ {
508
+ value: 'all',
509
+ label: 'Everything',
510
+ hint: 'Re-configure from scratch',
511
+ },
512
+ ],
513
+ });
514
+ assertNotCancel(fields);
515
+ let identity = {
516
+ name: current.name,
517
+ email: current.email,
518
+ fullName: current.full_name ?? '',
519
+ };
520
+ let server = {
521
+ imapHost: current.imap.host,
522
+ imapPort: current.imap.port,
523
+ imapTls: current.imap.tls,
524
+ smtpHost: current.smtp.host,
525
+ smtpPort: current.smtp.port,
526
+ smtpTls: current.smtp.tls,
527
+ smtpStarttls: current.smtp.starttls,
528
+ smtpPoolEnabled: current.smtp.pool?.enabled ?? true,
529
+ smtpPoolMaxConnections: current.smtp.pool?.max_connections ?? 1,
530
+ smtpPoolMaxMessages: current.smtp.pool?.max_messages ?? 100,
531
+ };
532
+ let creds = {
533
+ username: current.username ?? current.email,
534
+ password: current.password ?? '',
535
+ };
536
+ if (fields === 'identity' || fields === 'all') {
537
+ identity = await promptAccountIdentity(identity);
538
+ // If name changed, check for duplicates
539
+ if (identity.name !== current.name) {
540
+ if (accounts.some((a, i) => i !== accountIndex && a.name === identity.name)) {
541
+ log.error(`Account "${identity.name}" already exists.`);
542
+ cancel('Duplicate account name.');
543
+ return;
544
+ }
545
+ }
546
+ }
547
+ if (fields === 'servers' || fields === 'all') {
548
+ if (fields === 'all') {
549
+ server = await resolveServerSettings(identity.email, server);
550
+ }
551
+ else {
552
+ server = await promptServerSettings(server);
553
+ }
554
+ }
555
+ if (fields === 'credentials' || fields === 'all') {
556
+ creds = await promptCredentials({
557
+ username: creds.username,
558
+ email: identity.email,
559
+ });
560
+ }
561
+ // Test connections
562
+ const shouldTest = await confirm({
563
+ message: 'Test connections with updated settings?',
564
+ initialValue: true,
565
+ });
566
+ assertNotCancel(shouldTest);
567
+ if (shouldTest) {
568
+ const testAccount = buildTestAccount(identity, creds, server);
569
+ const ok = await testConnections(testAccount);
570
+ if (!ok)
571
+ return;
572
+ }
573
+ // Save updated account
574
+ const updatedAccount = buildRawAccount(identity, creds, server);
575
+ const updatedAccounts = [...accounts];
576
+ updatedAccounts[accountIndex] = updatedAccount;
577
+ const updatedConfig = {
578
+ ...config,
579
+ accounts: updatedAccounts,
580
+ };
581
+ AppConfigFileSchema.parse(updatedConfig);
582
+ await saveConfig(updatedConfig);
583
+ log.success(`Account "${identity.name}" updated. Config saved to ${CONFIG_FILE}`);
584
+ outro('Done!');
585
+ }
586
+ /**
587
+ * Delete an account with confirmation.
588
+ * Refuses to delete the last remaining account.
589
+ */
590
+ async function deleteAccount(nameArg) {
591
+ ensureInteractive();
592
+ intro('email-mcp › Delete Account');
593
+ const exists = await configExists();
594
+ if (!exists) {
595
+ log.error(`No config file found at: ${CONFIG_FILE}`);
596
+ cancel('Nothing to delete.');
597
+ return;
598
+ }
599
+ const config = await loadRawConfig();
600
+ const { accounts } = config;
601
+ if (accounts.length === 0) {
602
+ log.info('No accounts configured.');
603
+ cancel('Nothing to delete.');
604
+ return;
605
+ }
606
+ if (accounts.length === 1) {
607
+ log.error('Cannot delete the last account. At least one account must remain.');
608
+ log.info("Use 'email-mcp account edit' to modify it instead.");
609
+ cancel('Operation refused.');
610
+ return;
611
+ }
612
+ // Resolve which account to delete
613
+ let accountIndex;
614
+ if (nameArg) {
615
+ accountIndex = accounts.findIndex((a) => a.name === nameArg);
616
+ if (accountIndex === -1) {
617
+ log.error(`Account "${nameArg}" not found.`);
618
+ log.info(`Available accounts: ${accounts.map((a) => a.name).join(', ')}`);
619
+ cancel('Account not found.');
620
+ return;
621
+ }
622
+ }
623
+ else {
624
+ const chosen = await select({
625
+ message: 'Which account do you want to delete?',
626
+ options: accounts.map((a, i) => ({
627
+ value: i,
628
+ label: a.name,
629
+ hint: a.email,
630
+ })),
631
+ });
632
+ assertNotCancel(chosen);
633
+ accountIndex = chosen;
634
+ }
635
+ const target = accounts[accountIndex];
636
+ // Require explicit confirmation
637
+ const confirmed = await confirm({
638
+ message: `Delete account "${target.name}" (${target.email})? This cannot be undone.`,
639
+ initialValue: false,
640
+ });
641
+ if (isCancel(confirmed) || !confirmed) {
642
+ cancel('Deletion cancelled.');
643
+ return;
644
+ }
645
+ const updatedAccounts = accounts.filter((_, i) => i !== accountIndex);
646
+ const updatedConfig = {
647
+ ...config,
648
+ accounts: updatedAccounts,
649
+ };
650
+ AppConfigFileSchema.parse(updatedConfig);
651
+ await saveConfig(updatedConfig);
652
+ log.success(`Account "${target.name}" deleted. Config saved to ${CONFIG_FILE}`);
653
+ outro('Done!');
654
+ }
655
+ // ---------------------------------------------------------------------------
656
+ // Usage + dispatch
657
+ // ---------------------------------------------------------------------------
658
+ function printAccountUsage() {
659
+ console.log(`Usage: email-mcp account <subcommand>
660
+
661
+ Subcommands:
662
+ list List all configured accounts
663
+ add Add a new email account interactively
664
+ edit [name] Edit an existing account
665
+ delete [name] Remove an account
666
+
667
+ Examples:
668
+ email-mcp account list
669
+ email-mcp account add
670
+ email-mcp account edit personal
671
+ email-mcp account delete work
672
+ `);
673
+ }
674
+ export default async function runAccountCommand(subcommand, arg) {
675
+ try {
676
+ switch (subcommand) {
677
+ case 'list':
678
+ case 'ls':
679
+ await listAccounts();
680
+ return;
681
+ case 'add':
682
+ case 'new':
683
+ await addAccount();
684
+ return;
685
+ case 'edit':
686
+ await editAccount(arg);
687
+ return;
688
+ case 'delete':
689
+ case 'rm':
690
+ case 'remove':
691
+ await deleteAccount(arg);
692
+ return;
693
+ default:
694
+ printAccountUsage();
695
+ }
696
+ }
697
+ catch (err) {
698
+ if (err instanceof CancelledError)
699
+ return;
700
+ throw err;
701
+ }
702
+ }
703
+ //# sourceMappingURL=account-commands.js.map