@wirecat/cli-core 0.470.0 → 0.472.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.
- package/CHANGELOG.md +37 -0
- package/dist/cli/app-doctor-command.d.ts +107 -0
- package/dist/cli/app-doctor-command.js +101 -0
- package/dist/cli/app-mcp-command.d.ts +23 -0
- package/dist/cli/app-mcp-command.js +49 -0
- package/dist/cli/index.d.ts +4 -2
- package/dist/cli/index.js +2 -1
- package/dist/cli/messenger/mcp-command.d.ts +29 -3
- package/dist/cli/messenger/mcp-command.js +59 -39
- package/dist/cli/shared-command-exports.d.ts +348 -0
- package/dist/cli/shared-command-exports.js +4 -0
- package/dist/errors/reasons.js +1 -0
- package/dist/mail/bulk.d.ts +7 -0
- package/dist/mail/bulk.js +3 -0
- package/dist/mail/facts.d.ts +9 -0
- package/dist/mail/facts.js +22 -0
- package/dist/mail/folders.d.ts +10 -0
- package/dist/mail/folders.js +13 -0
- package/dist/mail/gone.d.ts +20 -0
- package/dist/mail/gone.js +53 -0
- package/dist/mail/headers.d.ts +43 -0
- package/dist/mail/headers.js +88 -0
- package/dist/mail/index.d.ts +17 -0
- package/dist/mail/index.js +16 -0
- package/dist/mail/parse.d.ts +24 -0
- package/dist/mail/parse.js +79 -0
- package/dist/mail/presets.d.ts +30 -0
- package/dist/mail/presets.js +95 -0
- package/dist/mail/probe.d.ts +49 -0
- package/dist/mail/probe.js +81 -0
- package/dist/mail/resume.d.ts +61 -0
- package/dist/mail/resume.js +61 -0
- package/dist/mail/rule-input.d.ts +14 -0
- package/dist/mail/rule-input.js +32 -0
- package/dist/mail/rule-tags.d.ts +40 -0
- package/dist/mail/rule-tags.js +87 -0
- package/dist/mail/rules.d.ts +83 -0
- package/dist/mail/rules.js +108 -0
- package/dist/mail/score.d.ts +67 -0
- package/dist/mail/score.js +174 -0
- package/dist/mail/sender-scores.d.ts +30 -0
- package/dist/mail/sender-scores.js +96 -0
- package/dist/mail/source.d.ts +67 -0
- package/dist/mail/source.js +1 -0
- package/dist/mail/stored.d.ts +7 -0
- package/dist/mail/stored.js +36 -0
- package/dist/mail/sync.d.ts +91 -0
- package/dist/mail/sync.js +572 -0
- package/dist/mail/testing/contract.d.ts +20 -0
- package/dist/mail/testing/contract.js +106 -0
- package/dist/mail/testing/fake.d.ts +61 -0
- package/dist/mail/testing/fake.js +136 -0
- package/dist/mail/testing/index.d.ts +2 -0
- package/dist/mail/testing/index.js +2 -0
- package/dist/mcp/agent.js +14 -2
- package/dist/mcp/app-server.d.ts +42 -0
- package/dist/mcp/app-server.js +162 -0
- package/dist/mcp/instructions.d.ts +12 -0
- package/dist/mcp/instructions.js +23 -0
- package/dist/mcp/server.d.ts +4 -0
- package/dist/mcp/server.js +2 -2
- package/package.json +12 -1
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { MessageStore } from "../store/index.js";
|
|
2
|
+
import { type MailRule } from "./rules.js";
|
|
3
|
+
import { type DomainAge } from "./score.js";
|
|
4
|
+
export interface LookupResult {
|
|
5
|
+
looked: number;
|
|
6
|
+
failed: number;
|
|
7
|
+
}
|
|
8
|
+
/** Registration dates of sender domains: the ones already known, and a lookup for the rest. */
|
|
9
|
+
export interface DomainAges {
|
|
10
|
+
known: () => ReadonlyMap<string, DomainAge>;
|
|
11
|
+
lookUp: (domains: readonly string[]) => Promise<LookupResult>;
|
|
12
|
+
}
|
|
13
|
+
/** Raised when the weights or the signals change, so every sender is scored again on the next sync. */
|
|
14
|
+
export declare const SCORE_VERSION = 4;
|
|
15
|
+
/** Signs of fraud adding up to this rank a sender's mail lower in search. */
|
|
16
|
+
export declare const DEMOTE_AT = 3;
|
|
17
|
+
/**
|
|
18
|
+
* Scores every sender of the account from the mail the store holds, as `memo mail inspect` scores a listing, and
|
|
19
|
+
* keeps the scores in the store, where search shows them. At most once a day, or at once after SCORE_VERSION
|
|
20
|
+
* changes. A sender in several groups (its own mail and a list) keeps the highest score. Domains not yet aged are
|
|
21
|
+
* looked up first, for senders heard from in the last two years.
|
|
22
|
+
*/
|
|
23
|
+
export declare const scoreSenders: (store: MessageStore, address: string, ages: DomainAges, now?: number, rules?: readonly MailRule[]) => Promise<{
|
|
24
|
+
scored: number;
|
|
25
|
+
looked: number;
|
|
26
|
+
failed: number;
|
|
27
|
+
demoted: number;
|
|
28
|
+
} | undefined>;
|
|
29
|
+
/** Every mail account's senders whose mail search ranks lower, as the last scoring left them. */
|
|
30
|
+
export declare const demotedSenders: (store: MessageStore) => Promise<Set<string>>;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { domainsToAge, groupKey, probeMail } from "./probe.js";
|
|
2
|
+
import { decide } from "./rules.js";
|
|
3
|
+
import { FRAUD_SIGNALS, FREE_MAIL, registrableOf, WEIGHTS } from "./score.js";
|
|
4
|
+
import { listedOf } from "./stored.js";
|
|
5
|
+
const SCORED = "mail_sender_scores";
|
|
6
|
+
/** Raised when the weights or the signals change, so every sender is scored again on the next sync. */
|
|
7
|
+
export const SCORE_VERSION = 4;
|
|
8
|
+
/** Signs of fraud adding up to this rank a sender's mail lower in search. */
|
|
9
|
+
export const DEMOTE_AT = 3;
|
|
10
|
+
const DAY = 86_400_000;
|
|
11
|
+
/** Only senders heard from this recently are looked up: an older sender's domain may have lapsed or changed hands. */
|
|
12
|
+
const RECENT = 2 * 365 * DAY;
|
|
13
|
+
/**
|
|
14
|
+
* Scores every sender of the account from the mail the store holds, as `memo mail inspect` scores a listing, and
|
|
15
|
+
* keeps the scores in the store, where search shows them. At most once a day, or at once after SCORE_VERSION
|
|
16
|
+
* changes. A sender in several groups (its own mail and a list) keeps the highest score. Domains not yet aged are
|
|
17
|
+
* looked up first, for senders heard from in the last two years.
|
|
18
|
+
*/
|
|
19
|
+
export const scoreSenders = async (store, address, ages, now = Date.now(), rules = []) => {
|
|
20
|
+
const key = { provider: "email", account: address.toLowerCase() };
|
|
21
|
+
const last = (await store.sync.syncState(key, SCORED))?.value;
|
|
22
|
+
const done = last === undefined ? undefined : JSON.parse(last);
|
|
23
|
+
if (done !== undefined && done.version === SCORE_VERSION && now - done.at < DAY)
|
|
24
|
+
return undefined;
|
|
25
|
+
const stored = await store.accounts.storedAccount(key).catch(() => null);
|
|
26
|
+
if (stored === null)
|
|
27
|
+
return undefined;
|
|
28
|
+
const items = (await store.mail.emailFacts({ accountId: stored.id })).map((email) => {
|
|
29
|
+
const item = listedOf(email);
|
|
30
|
+
return email.outgoing && !item.labels?.includes("Sent")
|
|
31
|
+
? { ...item, labels: [...(item.labels ?? []), "Sent"] }
|
|
32
|
+
: item;
|
|
33
|
+
});
|
|
34
|
+
// Each registry learns only its own domains' names; big mailbox providers and senders the owner wrote to are never asked.
|
|
35
|
+
const latest = new Map();
|
|
36
|
+
for (const item of items) {
|
|
37
|
+
if (item.headers === undefined)
|
|
38
|
+
continue;
|
|
39
|
+
const key = groupKey(item.headers);
|
|
40
|
+
if ((latest.get(key) ?? "") < item.receivedAt)
|
|
41
|
+
latest.set(key, item.receivedAt);
|
|
42
|
+
}
|
|
43
|
+
const since = new Date(now - RECENT).toISOString();
|
|
44
|
+
const recent = probeMail(items, Number.MAX_SAFE_INTEGER).items.filter((group) => (latest.get(groupKey({
|
|
45
|
+
...(group.from === null ? {} : { from: group.from }),
|
|
46
|
+
...(group.listId === null ? {} : { listId: group.listId }),
|
|
47
|
+
})) ?? "") >= since);
|
|
48
|
+
const lookups = await ages.lookUp(domainsToAge(recent, FREE_MAIL)).catch(() => ({ looked: 0, failed: 1 }));
|
|
49
|
+
const groups = new Map(probeMail(items, Number.MAX_SAFE_INTEGER, ages.known()).items.map((group) => [
|
|
50
|
+
groupKey({
|
|
51
|
+
...(group.from === null ? {} : { from: group.from }),
|
|
52
|
+
...(group.listId === null ? {} : { listId: group.listId }),
|
|
53
|
+
}),
|
|
54
|
+
group,
|
|
55
|
+
]));
|
|
56
|
+
// The owner's own: a domain they wrote to anyone at, their own mailbox's domain, or a sender a protect rule keeps.
|
|
57
|
+
const trusted = new Set([address, ...items.filter((item) => item.labels?.includes("Sent")).flatMap((item) => item.headers?.to ?? [])]
|
|
58
|
+
.map((to) => registrableOf(to))
|
|
59
|
+
.filter((domain) => !FREE_MAIL.has(domain)));
|
|
60
|
+
const protectedSenders = new Set(items.filter((item) => decide(rules, item)?.protected).flatMap((item) => item.headers?.from?.toLowerCase() ?? []));
|
|
61
|
+
const fraud = (signals) => signals.reduce((sum, signal) => sum + (FRAUD_SIGNALS.includes(signal) ? WEIGHTS[signal] : 0), 0);
|
|
62
|
+
const scores = new Map();
|
|
63
|
+
for (const item of items) {
|
|
64
|
+
const from = item.headers?.from?.toLowerCase();
|
|
65
|
+
const group = item.headers === undefined ? undefined : groups.get(groupKey(item.headers));
|
|
66
|
+
if (from === undefined || group === undefined)
|
|
67
|
+
continue;
|
|
68
|
+
const held = scores.get(from);
|
|
69
|
+
if (held === undefined || group.score > held.score)
|
|
70
|
+
scores.set(from, {
|
|
71
|
+
address: from,
|
|
72
|
+
score: group.score,
|
|
73
|
+
signals: group.signals,
|
|
74
|
+
demoted: fraud(group.signals) >= DEMOTE_AT &&
|
|
75
|
+
!group.signals.includes("replied") &&
|
|
76
|
+
!trusted.has(registrableOf(from)) &&
|
|
77
|
+
!protectedSenders.has(from),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
const demoted = [...scores.values()].filter((score) => score.demoted).map(({ address }) => address);
|
|
81
|
+
const written = await store.mail.setSenderScores(stored.id, [...scores.values()], now);
|
|
82
|
+
await store.sync.setSyncState(key, SCORED, JSON.stringify({ version: SCORE_VERSION, at: now, demoted }));
|
|
83
|
+
return { scored: written, ...lookups, demoted: demoted.length };
|
|
84
|
+
};
|
|
85
|
+
/** Every mail account's senders whose mail search ranks lower, as the last scoring left them. */
|
|
86
|
+
export const demotedSenders = async (store) => {
|
|
87
|
+
const senders = new Set();
|
|
88
|
+
for (const account of await store.accounts.accounts()) {
|
|
89
|
+
if (account.provider !== "email")
|
|
90
|
+
continue;
|
|
91
|
+
const value = (await store.sync.syncState(account, SCORED))?.value;
|
|
92
|
+
for (const sender of value === undefined ? [] : (JSON.parse(value).demoted ?? []))
|
|
93
|
+
senders.add(sender);
|
|
94
|
+
}
|
|
95
|
+
return senders;
|
|
96
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { MailHeaders } from "./headers.js";
|
|
2
|
+
import type { FileStamp } from "./resume.js";
|
|
3
|
+
export interface Listed {
|
|
4
|
+
uid: string;
|
|
5
|
+
/** Gmail's own ids: the same in every folder, and they survive a UID reset. */
|
|
6
|
+
messageId: string;
|
|
7
|
+
threadId: string;
|
|
8
|
+
receivedAt: string;
|
|
9
|
+
folder?: string;
|
|
10
|
+
/** Gmail labels, plain names; absent when the answer could not be read, so nothing is changed. */
|
|
11
|
+
labels?: string[];
|
|
12
|
+
seen?: boolean;
|
|
13
|
+
flagged?: boolean;
|
|
14
|
+
/** RFC822.SIZE in bytes, to size fetch batches. */
|
|
15
|
+
size?: number;
|
|
16
|
+
/** Read only when asked for; absent when not asked or unreadable, so no rule applies to it. */
|
|
17
|
+
headers?: MailHeaders;
|
|
18
|
+
}
|
|
19
|
+
export interface Searched {
|
|
20
|
+
/** Absent when the server does not say; then no UID is trusted across runs. */
|
|
21
|
+
validity?: string;
|
|
22
|
+
uids: string[];
|
|
23
|
+
}
|
|
24
|
+
/** Everything the import asks of a mail client, so a transport can be replaced without touching the sync. */
|
|
25
|
+
export interface MailSource {
|
|
26
|
+
/**
|
|
27
|
+
* Every message in `folder` received since `since`; all or nothing, since deletions are proved from it.
|
|
28
|
+
* `headers` also reads From, To, Cc and the list fields, for the probe and the owner's rules.
|
|
29
|
+
*/
|
|
30
|
+
list(since: Date, folder: string, options?: {
|
|
31
|
+
headers?: boolean;
|
|
32
|
+
searched?: Searched;
|
|
33
|
+
}): Promise<Listed[]>;
|
|
34
|
+
/**
|
|
35
|
+
* The folder's UIDs in the window and its UIDVALIDITY, without reading a message; `list` given them reads only
|
|
36
|
+
* those. Absent for a file, which has no UIDs.
|
|
37
|
+
*/
|
|
38
|
+
search?(since: Date, folder: string): Promise<Searched>;
|
|
39
|
+
/** A file's size and modification time, so an unchanged file read to the end is not read again. */
|
|
40
|
+
stamp?(): Promise<FileStamp>;
|
|
41
|
+
/**
|
|
42
|
+
* The messages of `folder` with these Gmail ids, listed as `list` lists them; a message no longer there is left
|
|
43
|
+
* out. Absent where messages have no Gmail id: a file, or plain IMAP.
|
|
44
|
+
*/
|
|
45
|
+
find?(messageIds: readonly string[], folder: string, options?: {
|
|
46
|
+
headers?: boolean;
|
|
47
|
+
}): Promise<Listed[]>;
|
|
48
|
+
/**
|
|
49
|
+
* Moves messages by Gmail id between folders on the server; ids `from` does not hold come back as missing.
|
|
50
|
+
* `known` gives each id's UID in `from`, already read, so none is looked up again.
|
|
51
|
+
*/
|
|
52
|
+
move?(messageIds: readonly string[], from: string, to: string, known?: ReadonlyMap<string, string>): Promise<{
|
|
53
|
+
moved: string[];
|
|
54
|
+
missing: string[];
|
|
55
|
+
}>;
|
|
56
|
+
/** All Mail and Trash under the names this account's language gives them. */
|
|
57
|
+
folders?(): Promise<{
|
|
58
|
+
all: string;
|
|
59
|
+
trash: string;
|
|
60
|
+
}>;
|
|
61
|
+
/** Which of these UIDs `folder` still holds. */
|
|
62
|
+
present?(uids: readonly string[], folder: string): Promise<string[]>;
|
|
63
|
+
/** Puts labels back on messages in `folder`, by Gmail id. */
|
|
64
|
+
label?(messageIds: readonly string[], folder: string, labels: readonly string[]): Promise<void>;
|
|
65
|
+
/** Each item's raw RFC 5322 bytes, in order; one that could not be read is an Error, not a failed call. */
|
|
66
|
+
fetchRaw(items: Listed[]): Promise<(Uint8Array | Error)[]>;
|
|
67
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { EmailFacts } from "../store/index.js";
|
|
2
|
+
import type { Listed } from "./source.js";
|
|
3
|
+
export type StoredMail = Listed & {
|
|
4
|
+
envelope: boolean;
|
|
5
|
+
};
|
|
6
|
+
/** A stored email in the shape a Gmail listing has; it has no UID until Gmail is asked. */
|
|
7
|
+
export declare const listedOf: (email: EmailFacts) => StoredMail;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { BULK } from "./bulk.js";
|
|
2
|
+
import { ENVELOPE } from "./rules.js";
|
|
3
|
+
/** A stored email in the shape a Gmail listing has; it has no UID until Gmail is asked. */
|
|
4
|
+
export const listedOf = (email) => {
|
|
5
|
+
const labels = email.mailboxes.filter(({ kind }) => kind === "label").map(({ name }) => name);
|
|
6
|
+
if (email.flagged && !labels.includes("Starred"))
|
|
7
|
+
labels.push("Starred");
|
|
8
|
+
return {
|
|
9
|
+
uid: "",
|
|
10
|
+
messageId: email.externalId,
|
|
11
|
+
threadId: "",
|
|
12
|
+
receivedAt: new Date(email.receivedAt ?? 0).toISOString(),
|
|
13
|
+
labels: labels.sort(),
|
|
14
|
+
seen: email.read,
|
|
15
|
+
flagged: email.flagged,
|
|
16
|
+
...(email.size === null ? {} : { size: email.size }),
|
|
17
|
+
headers: {
|
|
18
|
+
...(email.fromAddress === null ? {} : { from: email.fromAddress }),
|
|
19
|
+
...(email.fromName === null ? {} : { fromName: email.fromName }),
|
|
20
|
+
...(email.subject === null ? {} : { subject: email.subject }),
|
|
21
|
+
...(email.listId === null ? {} : { listId: email.listId }),
|
|
22
|
+
to: email.recipients,
|
|
23
|
+
bulk: email.mailboxes.some(({ kind }) => kind === BULK),
|
|
24
|
+
...(email.replyTo === null ? {} : { replyTo: email.replyTo }),
|
|
25
|
+
...(email.dkim === null && email.dmarc === null
|
|
26
|
+
? {}
|
|
27
|
+
: {
|
|
28
|
+
auth: {
|
|
29
|
+
...(email.dkim === null ? {} : { dkim: email.dkim }),
|
|
30
|
+
...(email.dmarc === null ? {} : { dmarc: email.dmarc }),
|
|
31
|
+
},
|
|
32
|
+
}),
|
|
33
|
+
},
|
|
34
|
+
envelope: email.mailboxes.some(({ kind }) => kind === ENVELOPE),
|
|
35
|
+
};
|
|
36
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { LoadEngine } from "../readers/types.js";
|
|
2
|
+
import type { AccountKey, MessageStore } from "../store/index.js";
|
|
3
|
+
import { type TagOutcome } from "./rule-tags.js";
|
|
4
|
+
import { type MailRule } from "./rules.js";
|
|
5
|
+
import type { DomainAge } from "./score.js";
|
|
6
|
+
import type { MailSource } from "./source.js";
|
|
7
|
+
export interface MailAccount {
|
|
8
|
+
/** The mailbox address: the store's account id. */
|
|
9
|
+
address: string;
|
|
10
|
+
mode?: "gmail" | "imap" | "mbox";
|
|
11
|
+
folders?: string[];
|
|
12
|
+
rules?: MailRule[];
|
|
13
|
+
}
|
|
14
|
+
/** What an index step reports, as memo's embedding does; the sync only passes it on. */
|
|
15
|
+
export interface MailIndexed {
|
|
16
|
+
chats: number;
|
|
17
|
+
chunks: number;
|
|
18
|
+
left: boolean;
|
|
19
|
+
notEmbedded?: unknown;
|
|
20
|
+
}
|
|
21
|
+
export interface MailSyncResult {
|
|
22
|
+
account: string;
|
|
23
|
+
since: string;
|
|
24
|
+
listed: number;
|
|
25
|
+
saved: number;
|
|
26
|
+
alreadyStored: number;
|
|
27
|
+
/** New emails a skip rule kept out: never read, never stored. */
|
|
28
|
+
skipped: number;
|
|
29
|
+
/** New emails an envelope rule recorded: sender, date, size and labels, never their text. */
|
|
30
|
+
envelopes: number;
|
|
31
|
+
deleted: number;
|
|
32
|
+
/** False when `max` stopped the run before every new message was read; the next run continues. */
|
|
33
|
+
complete: boolean;
|
|
34
|
+
/** Emails that could not be read or parsed; they are tried again next run. */
|
|
35
|
+
unreadable?: {
|
|
36
|
+
message: string;
|
|
37
|
+
reason: string;
|
|
38
|
+
}[];
|
|
39
|
+
/** Why stored messages missing at the source were left alone, when they were. */
|
|
40
|
+
deletionsSkipped?: string;
|
|
41
|
+
/** "resumed": only mail not done before was listed; "full": every message in the window was. */
|
|
42
|
+
pass?: "full" | "resumed";
|
|
43
|
+
indexed?: MailIndexed;
|
|
44
|
+
coverage?: {
|
|
45
|
+
folders: string[];
|
|
46
|
+
since: string;
|
|
47
|
+
until: string;
|
|
48
|
+
state: "window" | "partial";
|
|
49
|
+
identity: string;
|
|
50
|
+
};
|
|
51
|
+
attachments?: {
|
|
52
|
+
message: string;
|
|
53
|
+
part: number;
|
|
54
|
+
status: string;
|
|
55
|
+
}[];
|
|
56
|
+
/** Rule tags put on mail as an existing label the store pointed to. */
|
|
57
|
+
tagsReused?: TagOutcome["reused"];
|
|
58
|
+
/** Rule tags the store refused with nothing to reuse; that mail is stored untagged. */
|
|
59
|
+
tagsSkipped?: TagOutcome["skipped"];
|
|
60
|
+
/** `--retry-attachments`: emails read again for attachments a missing reader left unread. */
|
|
61
|
+
retried?: {
|
|
62
|
+
emails: number;
|
|
63
|
+
extracted: number;
|
|
64
|
+
stillFailed: number;
|
|
65
|
+
missing: number;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
export declare const MAX_ATTACHMENT_CHARS = 200000;
|
|
69
|
+
export interface MailSyncOptions {
|
|
70
|
+
account: MailAccount;
|
|
71
|
+
since: Date;
|
|
72
|
+
max?: number;
|
|
73
|
+
maxBodyChars?: number;
|
|
74
|
+
maxAttachmentChars?: number;
|
|
75
|
+
now?: () => number;
|
|
76
|
+
/** Domain registration dates already known, for the sender score a rule asks for. */
|
|
77
|
+
ages?: ReadonlyMap<string, DomainAge>;
|
|
78
|
+
loadEngine?: LoadEngine;
|
|
79
|
+
retryAttachments?: boolean;
|
|
80
|
+
/** List every message in the window, not only those an earlier run left: proves deletions, refreshes labels. */
|
|
81
|
+
rescan?: boolean;
|
|
82
|
+
/** A window smaller than this is listed whole every run: it costs seconds and keeps flags and deletions current. */
|
|
83
|
+
resumeFrom?: number;
|
|
84
|
+
/** Runs after the mail is saved, so chats and mail still waiting for vectors are indexed; its answer is `indexed`. */
|
|
85
|
+
index?: (store: MessageStore, key: AccountKey) => Promise<MailIndexed>;
|
|
86
|
+
/** Stops between batches of emails, after saving the resume point. */
|
|
87
|
+
signal?: AbortSignal;
|
|
88
|
+
}
|
|
89
|
+
export declare const syncMail: (source: MailSource, store: MessageStore, { account, since, max, maxBodyChars, maxAttachmentChars, now, ages, loadEngine, retryAttachments, rescan, resumeFrom, index, signal, }: MailSyncOptions) => Promise<MailSyncResult>;
|
|
90
|
+
/** Failures a newer install mends: the reader was missing, not the file at fault, so reading again can succeed. */
|
|
91
|
+
export declare const RETRYABLE_ATTACHMENTS: string[];
|