@diskd-ai/email-client-mcp 0.1.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 (81) hide show
  1. package/LICENSE +155 -0
  2. package/README.md +110 -0
  3. package/dist/config/loader.d.ts +12 -0
  4. package/dist/config/loader.js +42 -0
  5. package/dist/config/loader.js.map +1 -0
  6. package/dist/config/schema.d.ts +512 -0
  7. package/dist/config/schema.js +66 -0
  8. package/dist/config/schema.js.map +1 -0
  9. package/dist/domain/errors.d.ts +40 -0
  10. package/dist/domain/errors.js +46 -0
  11. package/dist/domain/errors.js.map +1 -0
  12. package/dist/domain/result.d.ts +30 -0
  13. package/dist/domain/result.js +12 -0
  14. package/dist/domain/result.js.map +1 -0
  15. package/dist/imap/fetch.d.ts +44 -0
  16. package/dist/imap/fetch.js +127 -0
  17. package/dist/imap/fetch.js.map +1 -0
  18. package/dist/imap/mapper.d.ts +70 -0
  19. package/dist/imap/mapper.js +140 -0
  20. package/dist/imap/mapper.js.map +1 -0
  21. package/dist/imap/pool.d.ts +26 -0
  22. package/dist/imap/pool.js +92 -0
  23. package/dist/imap/pool.js.map +1 -0
  24. package/dist/imap/virtualFolders.d.ts +11 -0
  25. package/dist/imap/virtualFolders.js +20 -0
  26. package/dist/imap/virtualFolders.js.map +1 -0
  27. package/dist/sdk/diskdClient.d.ts +21 -0
  28. package/dist/sdk/diskdClient.js +39 -0
  29. package/dist/sdk/diskdClient.js.map +1 -0
  30. package/dist/server.d.ts +17 -0
  31. package/dist/server.js +180 -0
  32. package/dist/server.js.map +1 -0
  33. package/dist/store/conventions.d.ts +31 -0
  34. package/dist/store/conventions.js +53 -0
  35. package/dist/store/conventions.js.map +1 -0
  36. package/dist/store/driveStore.d.ts +36 -0
  37. package/dist/store/driveStore.js +95 -0
  38. package/dist/store/driveStore.js.map +1 -0
  39. package/dist/store/payloadTypes.d.ts +62 -0
  40. package/dist/store/payloadTypes.js +11 -0
  41. package/dist/store/payloadTypes.js.map +1 -0
  42. package/dist/sync/backoff.d.ts +18 -0
  43. package/dist/sync/backoff.js +24 -0
  44. package/dist/sync/backoff.js.map +1 -0
  45. package/dist/sync/sync.d.ts +85 -0
  46. package/dist/sync/sync.js +383 -0
  47. package/dist/sync/sync.js.map +1 -0
  48. package/dist/sync/watcher.d.ts +43 -0
  49. package/dist/sync/watcher.js +141 -0
  50. package/dist/sync/watcher.js.map +1 -0
  51. package/dist/tools/bulkAction.d.ts +41 -0
  52. package/dist/tools/bulkAction.js +82 -0
  53. package/dist/tools/bulkAction.js.map +1 -0
  54. package/dist/tools/findMailboxFolder.d.ts +33 -0
  55. package/dist/tools/findMailboxFolder.js +45 -0
  56. package/dist/tools/findMailboxFolder.js.map +1 -0
  57. package/dist/tools/getEmail.d.ts +41 -0
  58. package/dist/tools/getEmail.js +74 -0
  59. package/dist/tools/getEmail.js.map +1 -0
  60. package/dist/tools/getEmails.d.ts +38 -0
  61. package/dist/tools/getEmails.js +72 -0
  62. package/dist/tools/getEmails.js.map +1 -0
  63. package/dist/tools/getWatcherStatus.d.ts +9 -0
  64. package/dist/tools/getWatcherStatus.js +8 -0
  65. package/dist/tools/getWatcherStatus.js.map +1 -0
  66. package/dist/tools/listAccounts.d.ts +19 -0
  67. package/dist/tools/listAccounts.js +18 -0
  68. package/dist/tools/listAccounts.js.map +1 -0
  69. package/dist/tools/listEmails.d.ts +70 -0
  70. package/dist/tools/listEmails.js +101 -0
  71. package/dist/tools/listEmails.js.map +1 -0
  72. package/dist/tools/listMailboxFolder.d.ts +33 -0
  73. package/dist/tools/listMailboxFolder.js +60 -0
  74. package/dist/tools/listMailboxFolder.js.map +1 -0
  75. package/dist/tools/moveEmail.d.ts +36 -0
  76. package/dist/tools/moveEmail.js +52 -0
  77. package/dist/tools/moveEmail.js.map +1 -0
  78. package/dist/tools/registry.d.ts +17 -0
  79. package/dist/tools/registry.js +64 -0
  80. package/dist/tools/registry.js.map +1 -0
  81. package/package.json +46 -0
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Result + Option ADTs. Domain returns `Result<E, T>`; adapters convert
3
+ * thrown exceptions into typed `Err`. Mirrors the project conventions
4
+ * from CODING_CONVENTIONS_TYPESCRIPT.md.
5
+ */
6
+ export const Ok = (value) => ({ tag: "Ok", value });
7
+ export const Err = (error) => ({ tag: "Err", error });
8
+ export const isOk = (r) => r.tag === "Ok";
9
+ export const isErr = (r) => r.tag === "Err";
10
+ export const Some = (value) => ({ tag: "Some", value });
11
+ export const None = { tag: "None" };
12
+ //# sourceMappingURL=result.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result.js","sourceRoot":"","sources":["../../src/domain/result.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,MAAM,CAAC,MAAM,EAAE,GAAG,CAAI,KAAQ,EAAoB,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAC5E,MAAM,CAAC,MAAM,GAAG,GAAG,CAAI,KAAQ,EAAoB,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;AAE9E,MAAM,CAAC,MAAM,IAAI,GAAG,CAAO,CAAe,EAAkD,EAAE,CAC5F,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC;AACjB,MAAM,CAAC,MAAM,KAAK,GAAG,CAAO,CAAe,EAAmD,EAAE,CAC9F,CAAC,CAAC,GAAG,KAAK,KAAK,CAAC;AAGlB,MAAM,CAAC,MAAM,IAAI,GAAG,CAAI,KAAQ,EAAa,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;AACzE,MAAM,CAAC,MAAM,IAAI,GAAkB,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * imapflow fetch helpers used by both the watcher (bulk sync) and the
3
+ * read tools (`get_email`, `get_emails`, `list_emails`). All helpers are
4
+ * BODY.PEEK by default so reads do not silently flip `\\Seen`.
5
+ *
6
+ * Body decoding strategy: we ask imapflow for `bodyParts` (the small
7
+ * `text/plain` and `text/html` pieces) instead of full source -- this
8
+ * keeps payloads bounded without requiring a separate MIME parser. The
9
+ * mapper truncates body byte-size on top.
10
+ */
11
+ import type { ImapFlow } from "imapflow";
12
+ import type { FetchedMessageLike } from "./mapper.js";
13
+ export type FolderStatusSnapshot = {
14
+ readonly uidValidity: number;
15
+ readonly uidNext: number;
16
+ readonly messages: number;
17
+ };
18
+ /**
19
+ * Run a callback under a per-folder IMAP lock. imapflow requires this
20
+ * for any FETCH/STORE/MOVE operation. The lock is released even if the
21
+ * callback throws.
22
+ */
23
+ export declare const withMailboxLock: <T>(client: ImapFlow, mailbox: string, fn: () => Promise<T>) => Promise<T>;
24
+ export declare const folderStatus: (client: ImapFlow, mailbox: string) => Promise<FolderStatusSnapshot>;
25
+ export type FetchedEnvelopeBundle = {
26
+ readonly imapMessage: FetchedMessageLike;
27
+ readonly bodyText: string | null;
28
+ readonly bodyHtml: string | null;
29
+ };
30
+ /**
31
+ * UID-range fetch for the watcher. Yields envelope + flags + bodyStructure
32
+ * + bodyParts(text/html) without setting `\\Seen`.
33
+ */
34
+ export declare function fetchUidRange(client: ImapFlow, fromUid: number, toUid: number): AsyncIterable<FetchedEnvelopeBundle>;
35
+ /**
36
+ * Fetch a single message by UID with BODY.PEEK semantics. Used by
37
+ * `get_email` and `get_emails`.
38
+ */
39
+ export declare const fetchOneByUid: (client: ImapFlow, uid: number) => Promise<FetchedEnvelopeBundle | null>;
40
+ /**
41
+ * Envelope-only fetch over a UID range. Used by `list_emails` and the
42
+ * watcher's flag-reconciliation pass (no body, no bodyStructure).
43
+ */
44
+ export declare function fetchEnvelopesUidRange(client: ImapFlow, fromUid: number, toUid: number): AsyncIterable<FetchedMessageLike>;
@@ -0,0 +1,127 @@
1
+ /**
2
+ * imapflow fetch helpers used by both the watcher (bulk sync) and the
3
+ * read tools (`get_email`, `get_emails`, `list_emails`). All helpers are
4
+ * BODY.PEEK by default so reads do not silently flip `\\Seen`.
5
+ *
6
+ * Body decoding strategy: we ask imapflow for `bodyParts` (the small
7
+ * `text/plain` and `text/html` pieces) instead of full source -- this
8
+ * keeps payloads bounded without requiring a separate MIME parser. The
9
+ * mapper truncates body byte-size on top.
10
+ */
11
+ const BODY_PARTS = ["text", "html"];
12
+ /**
13
+ * Run a callback under a per-folder IMAP lock. imapflow requires this
14
+ * for any FETCH/STORE/MOVE operation. The lock is released even if the
15
+ * callback throws.
16
+ */
17
+ export const withMailboxLock = async (client, mailbox, fn) => {
18
+ let lock = null;
19
+ try {
20
+ lock = await client.getMailboxLock(mailbox);
21
+ return await fn();
22
+ }
23
+ finally {
24
+ if (lock !== null)
25
+ lock.release();
26
+ }
27
+ };
28
+ export const folderStatus = async (client, mailbox) => {
29
+ const status = await client.status(mailbox, {
30
+ uidValidity: true,
31
+ uidNext: true,
32
+ messages: true,
33
+ });
34
+ return {
35
+ uidValidity: Number(status.uidValidity ?? 0),
36
+ uidNext: Number(status.uidNext ?? 1),
37
+ messages: Number(status.messages ?? 0),
38
+ };
39
+ };
40
+ const decodeBuffer = (b) => {
41
+ if (!b || b.length === 0)
42
+ return null;
43
+ return b.toString("utf8");
44
+ };
45
+ const extractText = (msg) => {
46
+ const candidate = msg.bodyParts?.get("text") ?? msg.bodyParts?.get("1");
47
+ return decodeBuffer(candidate);
48
+ };
49
+ const extractHtml = (msg) => {
50
+ const candidate = msg.bodyParts?.get("html") ?? msg.bodyParts?.get("1.2");
51
+ return decodeBuffer(candidate);
52
+ };
53
+ const toInternalDate = (raw) => {
54
+ if (raw instanceof Date)
55
+ return raw;
56
+ if (typeof raw === "string" && raw.length > 0) {
57
+ const d = new Date(raw);
58
+ return Number.isNaN(d.getTime()) ? undefined : d;
59
+ }
60
+ return undefined;
61
+ };
62
+ const toLike = (msg) => ({
63
+ uid: msg.uid,
64
+ flags: msg.flags ?? new Set(),
65
+ envelope: msg.envelope,
66
+ bodyStructure: msg.bodyStructure,
67
+ internalDate: toInternalDate(msg.internalDate),
68
+ });
69
+ /**
70
+ * UID-range fetch for the watcher. Yields envelope + flags + bodyStructure
71
+ * + bodyParts(text/html) without setting `\\Seen`.
72
+ */
73
+ export async function* fetchUidRange(client, fromUid, toUid) {
74
+ const range = `${fromUid}:${toUid}`;
75
+ for await (const msg of client.fetch(range, {
76
+ uid: true,
77
+ flags: true,
78
+ envelope: true,
79
+ bodyStructure: true,
80
+ bodyParts: BODY_PARTS,
81
+ internalDate: true,
82
+ }, { uid: true })) {
83
+ yield {
84
+ imapMessage: toLike(msg),
85
+ bodyText: extractText(msg),
86
+ bodyHtml: extractHtml(msg),
87
+ };
88
+ }
89
+ }
90
+ /**
91
+ * Fetch a single message by UID with BODY.PEEK semantics. Used by
92
+ * `get_email` and `get_emails`.
93
+ */
94
+ export const fetchOneByUid = async (client, uid) => {
95
+ const msg = await client.fetchOne(String(uid), {
96
+ uid: true,
97
+ flags: true,
98
+ envelope: true,
99
+ bodyStructure: true,
100
+ bodyParts: BODY_PARTS,
101
+ internalDate: true,
102
+ }, { uid: true });
103
+ if (!msg)
104
+ return null;
105
+ return {
106
+ imapMessage: toLike(msg),
107
+ bodyText: extractText(msg),
108
+ bodyHtml: extractHtml(msg),
109
+ };
110
+ };
111
+ /**
112
+ * Envelope-only fetch over a UID range. Used by `list_emails` and the
113
+ * watcher's flag-reconciliation pass (no body, no bodyStructure).
114
+ */
115
+ export async function* fetchEnvelopesUidRange(client, fromUid, toUid) {
116
+ const range = `${fromUid}:${toUid}`;
117
+ for await (const msg of client.fetch(range, {
118
+ uid: true,
119
+ flags: true,
120
+ envelope: true,
121
+ bodyStructure: true,
122
+ internalDate: true,
123
+ }, { uid: true })) {
124
+ yield toLike(msg);
125
+ }
126
+ }
127
+ //# sourceMappingURL=fetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../src/imap/fetch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Pure mapping from imapflow `FetchMessageObject` to our opaque
3
+ * `StoredEmailPayload`. Kept I/O-free and free of imapflow runtime
4
+ * imports so unit tests can build payload-shaped objects without a
5
+ * real IMAP connection.
6
+ */
7
+ import type { StoredEmailPayload } from "../store/payloadTypes.js";
8
+ type ImapAddressLike = {
9
+ readonly name?: string | undefined;
10
+ readonly address?: string | undefined;
11
+ };
12
+ type ImapEnvelopeLike = {
13
+ readonly date?: Date | string | undefined;
14
+ readonly subject?: string | undefined;
15
+ readonly messageId?: string | undefined;
16
+ readonly inReplyTo?: string | undefined;
17
+ readonly from?: readonly ImapAddressLike[] | undefined;
18
+ readonly to?: readonly ImapAddressLike[] | undefined;
19
+ readonly cc?: readonly ImapAddressLike[] | undefined;
20
+ };
21
+ type ImapBodyStructureLike = {
22
+ readonly type?: string | undefined;
23
+ readonly part?: string | undefined;
24
+ readonly disposition?: string | undefined;
25
+ readonly dispositionParameters?: {
26
+ readonly filename?: string | undefined;
27
+ } | undefined;
28
+ readonly parameters?: {
29
+ readonly name?: string | undefined;
30
+ } | undefined;
31
+ readonly size?: number | undefined;
32
+ readonly childNodes?: readonly ImapBodyStructureLike[] | undefined;
33
+ };
34
+ export type FetchedMessageLike = {
35
+ readonly uid: number;
36
+ readonly flags?: ReadonlySet<string> | readonly string[] | undefined;
37
+ readonly labels?: ReadonlySet<string> | readonly string[] | undefined;
38
+ readonly envelope?: ImapEnvelopeLike | undefined;
39
+ readonly bodyStructure?: ImapBodyStructureLike | undefined;
40
+ readonly source?: Buffer | undefined;
41
+ readonly internalDate?: Date | undefined;
42
+ };
43
+ export type MapperParams = {
44
+ readonly accountId: string;
45
+ readonly mailbox: string;
46
+ readonly uidValidity: number;
47
+ readonly fetchedAt: Date;
48
+ readonly bodyText: string | null;
49
+ readonly bodyHtml: string | null;
50
+ readonly truncated: boolean;
51
+ };
52
+ /**
53
+ * Build a `StoredEmailPayload` from imapflow fetch output and decoded
54
+ * body parts. Pure: no imapflow types are imported, so tests can pass
55
+ * shaped POJOs.
56
+ */
57
+ export declare const toStoredPayload: (msg: FetchedMessageLike, params: MapperParams) => StoredEmailPayload;
58
+ /**
59
+ * Strip `text/html` to an approximate plain-text representation. Conservative:
60
+ * removes scripts/styles, replaces tags with spaces, decodes a small set of
61
+ * named entities. Used by the `format="text"` option of get_email/get_emails.
62
+ */
63
+ export declare const htmlToText: (html: string) => string;
64
+ /**
65
+ * Strip quoted reply blocks and signatures from a plain-text body. Heuristic
66
+ * but conservative: cuts at the first `On <date>, <name> wrote:` block, or at
67
+ * a leading-`>` quote run, or at a `-- ` signature delimiter.
68
+ */
69
+ export declare const stripQuotedAndSignature: (text: string) => string;
70
+ export {};
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Pure mapping from imapflow `FetchMessageObject` to our opaque
3
+ * `StoredEmailPayload`. Kept I/O-free and free of imapflow runtime
4
+ * imports so unit tests can build payload-shaped objects without a
5
+ * real IMAP connection.
6
+ */
7
+ const MAX_BODY_BYTES = 1_048_576; // 1 MiB cap per body part to keep store payloads small
8
+ const SNIPPET_CHARS = 200;
9
+ const toAddr = (a) => ({
10
+ name: a.name && a.name.length > 0 ? a.name : null,
11
+ address: a.address ?? "",
12
+ });
13
+ const toAddrArr = (xs) => (xs ?? []).map(toAddr).filter((a) => a.address.length > 0);
14
+ const toIso = (d, fallback) => {
15
+ if (d instanceof Date)
16
+ return d.toISOString();
17
+ if (typeof d === "string" && d.length > 0) {
18
+ const parsed = new Date(d);
19
+ if (!Number.isNaN(parsed.getTime()))
20
+ return parsed.toISOString();
21
+ }
22
+ return fallback.toISOString();
23
+ };
24
+ const flagArray = (src) => {
25
+ if (!src)
26
+ return [];
27
+ if (src instanceof Set)
28
+ return Array.from(src);
29
+ return [...src];
30
+ };
31
+ const collectAttachments = (node, out) => {
32
+ if (!node)
33
+ return;
34
+ const isAttachment = node.disposition === "attachment" ||
35
+ (node.disposition === "inline" &&
36
+ (node.dispositionParameters?.filename ?? node.parameters?.name));
37
+ const filename = node.dispositionParameters?.filename ?? node.parameters?.name;
38
+ if (isAttachment && filename) {
39
+ out.push({
40
+ filename,
41
+ contentType: node.type ?? "application/octet-stream",
42
+ sizeBytes: node.size ?? 0,
43
+ partId: node.part ?? "",
44
+ });
45
+ }
46
+ for (const child of node.childNodes ?? []) {
47
+ collectAttachments(child, out);
48
+ }
49
+ };
50
+ const truncateBody = (body) => {
51
+ if (body === null)
52
+ return { value: null, truncated: false };
53
+ const bytes = Buffer.byteLength(body, "utf8");
54
+ if (bytes <= MAX_BODY_BYTES)
55
+ return { value: body, truncated: false };
56
+ // Truncate to MAX_BODY_BYTES bytes (UTF-8 safe).
57
+ const buf = Buffer.from(body, "utf8").subarray(0, MAX_BODY_BYTES);
58
+ return { value: buf.toString("utf8"), truncated: true };
59
+ };
60
+ const buildSnippet = (text) => {
61
+ if (text === null)
62
+ return "";
63
+ const trimmed = text.replace(/\s+/g, " ").trim();
64
+ return trimmed.slice(0, SNIPPET_CHARS);
65
+ };
66
+ /**
67
+ * Build a `StoredEmailPayload` from imapflow fetch output and decoded
68
+ * body parts. Pure: no imapflow types are imported, so tests can pass
69
+ * shaped POJOs.
70
+ */
71
+ export const toStoredPayload = (msg, params) => {
72
+ const env = msg.envelope ?? {};
73
+ const attachments = [];
74
+ collectAttachments(msg.bodyStructure, attachments);
75
+ const bodyText = truncateBody(params.bodyText);
76
+ const bodyHtml = truncateBody(params.bodyHtml);
77
+ const fallbackDate = msg.internalDate ?? params.fetchedAt;
78
+ return {
79
+ accountId: params.accountId,
80
+ mailbox: params.mailbox,
81
+ uid: msg.uid,
82
+ uidValidity: params.uidValidity,
83
+ messageId: env.messageId ?? null,
84
+ inReplyTo: env.inReplyTo ?? null,
85
+ references: [],
86
+ from: env.from && env.from.length > 0 ? toAddr(env.from[0]) : null,
87
+ to: toAddrArr(env.to),
88
+ cc: toAddrArr(env.cc),
89
+ subject: env.subject ?? "",
90
+ date: toIso(env.date, fallbackDate),
91
+ flags: flagArray(msg.flags),
92
+ labels: flagArray(msg.labels),
93
+ hasAttachments: attachments.length > 0,
94
+ attachments,
95
+ snippet: buildSnippet(bodyText.value),
96
+ bodyText: bodyText.value,
97
+ bodyHtml: bodyHtml.value,
98
+ truncated: bodyText.truncated || bodyHtml.truncated || params.truncated,
99
+ fetchedAt: params.fetchedAt.toISOString(),
100
+ };
101
+ };
102
+ /**
103
+ * Strip `text/html` to an approximate plain-text representation. Conservative:
104
+ * removes scripts/styles, replaces tags with spaces, decodes a small set of
105
+ * named entities. Used by the `format="text"` option of get_email/get_emails.
106
+ */
107
+ export const htmlToText = (html) => {
108
+ const noScript = html
109
+ .replace(/<script[\s\S]*?<\/script>/gi, "")
110
+ .replace(/<style[\s\S]*?<\/style>/gi, "");
111
+ const noTags = noScript.replace(/<[^>]+>/g, " ");
112
+ const decoded = noTags
113
+ .replace(/&nbsp;/g, " ")
114
+ .replace(/&amp;/g, "&")
115
+ .replace(/&lt;/g, "<")
116
+ .replace(/&gt;/g, ">")
117
+ .replace(/&quot;/g, '"')
118
+ .replace(/&#39;/g, "'");
119
+ return decoded.replace(/\s+/g, " ").trim();
120
+ };
121
+ /**
122
+ * Strip quoted reply blocks and signatures from a plain-text body. Heuristic
123
+ * but conservative: cuts at the first `On <date>, <name> wrote:` block, or at
124
+ * a leading-`>` quote run, or at a `-- ` signature delimiter.
125
+ */
126
+ export const stripQuotedAndSignature = (text) => {
127
+ const lines = text.split(/\r?\n/);
128
+ const out = [];
129
+ for (const line of lines) {
130
+ if (/^-- $/.test(line))
131
+ break;
132
+ if (/^On\s.+wrote:$/.test(line))
133
+ break;
134
+ if (/^>+\s/.test(line))
135
+ break;
136
+ out.push(line);
137
+ }
138
+ return out.join("\n").trimEnd();
139
+ };
140
+ //# sourceMappingURL=mapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mapper.js","sourceRoot":"","sources":["../../src/imap/mapper.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Per-account `ImapFlow` connection cache.
3
+ *
4
+ * Reliability properties:
5
+ * - Connections are created lazily and reused across tool calls and
6
+ * sync ticks. Lock acquisition (per imapflow's getMailboxLock) is
7
+ * the only way folder operations occur, so two concurrent callers
8
+ * can share one socket without races.
9
+ * - On a connection that has dropped (`usable === false`), the next
10
+ * `forAccount(...)` call rebuilds it.
11
+ * - OAuth refresh: imapflow accepts an `accessToken` for XOAUTH2;
12
+ * the pool keeps a per-account "fetcher" that swaps the access
13
+ * token in-place at reconnect time. The provider-side refresh is
14
+ * a follow-up (see SDK gap notes); for v1 the password path is
15
+ * fully wired and OAuth runs against a long-lived access token.
16
+ */
17
+ import { ImapFlow } from "imapflow";
18
+ import { type Account } from "../config/schema.js";
19
+ import { type ImapError } from "../domain/errors.js";
20
+ import { type Result } from "../domain/result.js";
21
+ export type ImapPool = {
22
+ readonly forAccount: (accountId: string) => Promise<Result<ImapError, ImapFlow>>;
23
+ readonly closeAll: () => Promise<void>;
24
+ readonly accountIds: readonly string[];
25
+ };
26
+ export declare const buildImapPool: (accounts: readonly Account[]) => ImapPool;
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Per-account `ImapFlow` connection cache.
3
+ *
4
+ * Reliability properties:
5
+ * - Connections are created lazily and reused across tool calls and
6
+ * sync ticks. Lock acquisition (per imapflow's getMailboxLock) is
7
+ * the only way folder operations occur, so two concurrent callers
8
+ * can share one socket without races.
9
+ * - On a connection that has dropped (`usable === false`), the next
10
+ * `forAccount(...)` call rebuilds it.
11
+ * - OAuth refresh: imapflow accepts an `accessToken` for XOAUTH2;
12
+ * the pool keeps a per-account "fetcher" that swaps the access
13
+ * token in-place at reconnect time. The provider-side refresh is
14
+ * a follow-up (see SDK gap notes); for v1 the password path is
15
+ * fully wired and OAuth runs against a long-lived access token.
16
+ */
17
+ import { ImapFlow } from "imapflow";
18
+ import { isOAuthAccount } from "../config/schema.js";
19
+ import { imapError } from "../domain/errors.js";
20
+ import { Err, Ok } from "../domain/result.js";
21
+ const buildClient = (acct) => {
22
+ if (isOAuthAccount(acct)) {
23
+ return new ImapFlow({
24
+ host: acct.imap.host,
25
+ port: acct.imap.port,
26
+ secure: acct.imap.tls,
27
+ tls: { rejectUnauthorized: acct.imap.verify_ssl },
28
+ auth: {
29
+ user: acct.email,
30
+ accessToken: acct.oauth2.refresh_token,
31
+ },
32
+ logger: false,
33
+ });
34
+ }
35
+ return new ImapFlow({
36
+ host: acct.imap.host,
37
+ port: acct.imap.port,
38
+ secure: acct.imap.tls,
39
+ tls: { rejectUnauthorized: acct.imap.verify_ssl },
40
+ auth: {
41
+ user: acct.username ?? acct.email,
42
+ pass: acct.password,
43
+ },
44
+ logger: false,
45
+ });
46
+ };
47
+ export const buildImapPool = (accounts) => {
48
+ const byId = new Map();
49
+ for (const a of accounts)
50
+ byId.set(a.name, a);
51
+ const liveById = new Map();
52
+ const forAccount = async (accountId) => {
53
+ const acct = byId.get(accountId);
54
+ if (!acct)
55
+ return Err(imapError(accountId, "unknown account id"));
56
+ const cached = liveById.get(accountId);
57
+ if (cached && cached.usable)
58
+ return Ok(cached);
59
+ if (cached && !cached.usable) {
60
+ try {
61
+ await cached.logout();
62
+ }
63
+ catch {
64
+ // ignore: connection was already broken
65
+ }
66
+ liveById.delete(accountId);
67
+ }
68
+ const client = buildClient(acct);
69
+ try {
70
+ await client.connect();
71
+ }
72
+ catch (cause) {
73
+ return Err(imapError(accountId, `connect failed: ${cause?.message ?? String(cause)}`, cause));
74
+ }
75
+ liveById.set(accountId, client);
76
+ return Ok(client);
77
+ };
78
+ const closeAll = async () => {
79
+ const closes = Array.from(liveById.values()).map(async (c) => {
80
+ try {
81
+ await c.logout();
82
+ }
83
+ catch {
84
+ // ignore on shutdown
85
+ }
86
+ });
87
+ await Promise.all(closes);
88
+ liveById.clear();
89
+ };
90
+ return { forAccount, closeAll, accountIds: Array.from(byId.keys()) };
91
+ };
92
+ //# sourceMappingURL=pool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pool.js","sourceRoot":"","sources":["../../src/imap/pool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Allowlist of well-known IMAP "virtual" folders. move_email and
3
+ * bulk_action#move refuse these because IMAP MOVE on a virtual mailbox
4
+ * either fails or moves a different message than the agent intended.
5
+ *
6
+ * The list is intentionally short and explicit; `find_mailbox_folder`
7
+ * is the documented path to resolve real folders for messages
8
+ * discovered via virtuals (e.g. Gmail's "[Gmail]/All Mail").
9
+ */
10
+ export declare const isVirtualFolderPath: (path: string) => boolean;
11
+ export declare const isVirtualSpecialUse: (specialUse: string | undefined | null) => boolean;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Allowlist of well-known IMAP "virtual" folders. move_email and
3
+ * bulk_action#move refuse these because IMAP MOVE on a virtual mailbox
4
+ * either fails or moves a different message than the agent intended.
5
+ *
6
+ * The list is intentionally short and explicit; `find_mailbox_folder`
7
+ * is the documented path to resolve real folders for messages
8
+ * discovered via virtuals (e.g. Gmail's "[Gmail]/All Mail").
9
+ */
10
+ const VIRTUAL_FOLDER_PATHS = new Set([
11
+ "[Gmail]/All Mail",
12
+ "[Gmail]/Starred",
13
+ "[Gmail]/Important",
14
+ "INBOX/All Mail",
15
+ "All Mail",
16
+ ]);
17
+ const VIRTUAL_SPECIAL_USE = new Set(["\\All", "\\Flagged", "\\Important"]);
18
+ export const isVirtualFolderPath = (path) => VIRTUAL_FOLDER_PATHS.has(path);
19
+ export const isVirtualSpecialUse = (specialUse) => specialUse !== undefined && specialUse !== null && VIRTUAL_SPECIAL_USE.has(specialUse);
20
+ //# sourceMappingURL=virtualFolders.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"virtualFolders.js","sourceRoot":"","sources":["../../src/imap/virtualFolders.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAS;IAC3C,kBAAkB;IAClB,iBAAiB;IACjB,mBAAmB;IACnB,gBAAgB;IAChB,UAAU;CACX,CAAC,CAAC;AAEH,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAS,CAAC,OAAO,EAAE,WAAW,EAAE,aAAa,CAAC,CAAC,CAAC;AAEnF,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,IAAY,EAAW,EAAE,CAAC,oBAAoB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAE7F,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,UAAqC,EAAW,EAAE,CACpF,UAAU,KAAK,SAAS,IAAI,UAAU,KAAK,IAAI,IAAI,mBAAmB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Build the @diskd/sdk client used by the drive-backed messages store.
3
+ *
4
+ * SDK constraint observed in 5.1.x: `APIS_API_KEY` and `APIS_BASE_URL`
5
+ * are read from `process.env` only -- there is no constructor param.
6
+ * We therefore copy the TOML `[sdk]` block into env *before* calling
7
+ * `diskd.auth.apiKey()`. This is a small SDK gap (would prefer explicit
8
+ * params); documented in the design plan and called out here.
9
+ *
10
+ * Workspace id falls back to `MCP_HUB_WORKSPACE_ID` (injected by mcp-hub
11
+ * k8s-gateway specs) so deployments don't have to encode workspaceId twice.
12
+ */
13
+ import { diskd } from "@diskd/sdk";
14
+ import type { SdkSettings } from "../config/schema.js";
15
+ import { type ConfigError } from "../domain/errors.js";
16
+ import { type Result } from "../domain/result.js";
17
+ export type DiskdRuntime = {
18
+ readonly workspaceId: string;
19
+ readonly messagesStore: ReturnType<typeof diskd.os.messagesStore>;
20
+ };
21
+ export declare const buildDiskd: (sdkSettings: SdkSettings, envWorkspaceId: string | undefined) => Result<ConfigError, DiskdRuntime>;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Build the @diskd/sdk client used by the drive-backed messages store.
3
+ *
4
+ * SDK constraint observed in 5.1.x: `APIS_API_KEY` and `APIS_BASE_URL`
5
+ * are read from `process.env` only -- there is no constructor param.
6
+ * We therefore copy the TOML `[sdk]` block into env *before* calling
7
+ * `diskd.auth.apiKey()`. This is a small SDK gap (would prefer explicit
8
+ * params); documented in the design plan and called out here.
9
+ *
10
+ * Workspace id falls back to `MCP_HUB_WORKSPACE_ID` (injected by mcp-hub
11
+ * k8s-gateway specs) so deployments don't have to encode workspaceId twice.
12
+ */
13
+ import { diskd } from "@diskd/sdk";
14
+ import { configError } from "../domain/errors.js";
15
+ import { Err, Ok } from "../domain/result.js";
16
+ const ensureEnv = (key, value) => {
17
+ // Only set if unset, so an explicit env value (e.g. from mcp-hub) wins
18
+ // over the TOML when present.
19
+ const current = process.env[key];
20
+ if (current === undefined || current.length === 0) {
21
+ process.env[key] = value;
22
+ }
23
+ };
24
+ export const buildDiskd = (sdkSettings, envWorkspaceId) => {
25
+ const workspaceId = sdkSettings.workspace_id ?? envWorkspaceId;
26
+ if (workspaceId === undefined || workspaceId.length === 0) {
27
+ return Err(configError("workspace_id is missing -- set [sdk].workspace_id in TOML or MCP_HUB_WORKSPACE_ID in env"));
28
+ }
29
+ ensureEnv("APIS_API_KEY", sdkSettings.api_key);
30
+ if (sdkSettings.base_url)
31
+ ensureEnv("APIS_BASE_URL", sdkSettings.base_url);
32
+ if (process.env.APIS_BASE_URL === undefined || process.env.APIS_BASE_URL.length === 0) {
33
+ return Err(configError("APIS_BASE_URL is not set -- provide [sdk].base_url in TOML or APIS_BASE_URL in env"));
34
+ }
35
+ const auth = diskd.auth.apiKey({ workspaceId });
36
+ const messagesStore = diskd.os.messagesStore({ auth });
37
+ return Ok({ workspaceId, messagesStore });
38
+ };
39
+ //# sourceMappingURL=diskdClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diskdClient.js","sourceRoot":"","sources":["../../src/sdk/diskdClient.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAoB,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,EAAE,GAAG,EAAE,EAAE,EAAe,MAAM,qBAAqB,CAAC;AAO3D,MAAM,SAAS,GAAG,CAAC,GAAW,EAAE,KAAa,EAAQ,EAAE;IACrD,uEAAuE;IACvE,8BAA8B;IAC9B,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClD,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IAC3B,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAG,CACxB,WAAwB,EACxB,cAAkC,EACC,EAAE;IACrC,MAAM,WAAW,GAAG,WAAW,CAAC,YAAY,IAAI,cAAc,CAAC;IAC/D,IAAI,WAAW,KAAK,SAAS,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1D,OAAO,GAAG,CACR,WAAW,CACT,0FAA0F,CAC3F,CACF,CAAC;IACJ,CAAC;IAED,SAAS,CAAC,cAAc,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IAC/C,IAAI,WAAW,CAAC,QAAQ;QAAE,SAAS,CAAC,eAAe,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC3E,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,KAAK,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtF,OAAO,GAAG,CACR,WAAW,CACT,oFAAoF,CACrF,CACF,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC;IAChD,MAAM,aAAa,GAAG,KAAK,CAAC,EAAE,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,OAAO,EAAE,CAAC,EAAE,WAAW,EAAE,aAAa,EAAE,CAAC,CAAC;AAC5C,CAAC,CAAC"}