@wirecat/cli-core 0.533.0 → 0.535.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 CHANGED
@@ -7,6 +7,21 @@ shape of this file.
7
7
 
8
8
  ## Unreleased
9
9
 
10
+ ## 0.535.0 — 01.11.2026
11
+
12
+ ### Added
13
+
14
+ - **`syncMail` takes `saveCoverage: false`** for a spot read, such as a CLI storing the copy of a message it just
15
+ sent: the mail is saved, and the coverage that memo reports for the archive stays what the last full run set.
16
+
17
+ ## 0.534.0 — 01.11.2026
18
+
19
+ ### Added
20
+
21
+ - **`parseMail` also returns `messageId`, `references`, `replyTo` and `date`**, and takes a third argument, the most
22
+ attachments to keep (`MAX_ATTACHMENTS`, 50, by default), so a command that lists one message's files can ask for all
23
+ of them.
24
+
10
25
  ## 0.533.0 — 01.11.2026
11
26
 
12
27
  ### Added
@@ -6,7 +6,7 @@ export { dropMissing, type Gone, tooManyGone } from "./gone.js";
6
6
  export { type AuthVerdicts, HEADER_FIELDS, headersOf, type MailHeaders, mailHeadersOf } from "./headers.js";
7
7
  export { type MailIdsInput, mailIds } from "./ids.js";
8
8
  export { decodedLabelName, decodeMailboxName, labelName, systemLabel } from "./labels.js";
9
- export { type Address, htmlToText, looksLikeHtml, MAX_BODY_CHARS, type ParsedMail, parseMail, } from "./parse.js";
9
+ export { type Address, htmlToText, looksLikeHtml, MAX_ATTACHMENTS, MAX_BODY_CHARS, type ParsedMail, parseMail, } from "./parse.js";
10
10
  export { PRESET_NAMES, PRESETS, type PresetName, presetMatches } from "./presets.js";
11
11
  export { domainsToAge, groupKey, type Probe, type ProbeGroup, probeMail, SUGGEST_RULE } from "./probe.js";
12
12
  export { type MailFolderProgress, mailProgress } from "./progress.js";
@@ -6,7 +6,7 @@ export { dropMissing, tooManyGone } from "./gone.js";
6
6
  export { HEADER_FIELDS, headersOf, mailHeadersOf } from "./headers.js";
7
7
  export { mailIds } from "./ids.js";
8
8
  export { decodedLabelName, decodeMailboxName, labelName, systemLabel } from "./labels.js";
9
- export { htmlToText, looksLikeHtml, MAX_BODY_CHARS, parseMail, } from "./parse.js";
9
+ export { htmlToText, looksLikeHtml, MAX_ATTACHMENTS, MAX_BODY_CHARS, parseMail, } from "./parse.js";
10
10
  export { PRESET_NAMES, PRESETS, presetMatches } from "./presets.js";
11
11
  export { domainsToAge, groupKey, probeMail, SUGGEST_RULE } from "./probe.js";
12
12
  export { mailProgress } from "./progress.js";
@@ -8,6 +8,12 @@ export interface ParsedMail {
8
8
  to: Address[];
9
9
  cc: Address[];
10
10
  bcc: Address[];
11
+ replyTo: Address[];
12
+ date: string | null;
13
+ /** Message-ID as the first `<id>` the header holds, brackets kept; the header as written when it holds none. */
14
+ messageId: string | null;
15
+ /** Every `<id>` of References, in the header's order, brackets kept. */
16
+ references: string[];
11
17
  attachments: {
12
18
  id: number;
13
19
  name: string;
@@ -17,8 +23,10 @@ export interface ParsedMail {
17
23
  text: string;
18
24
  }
19
25
  export declare const MAX_BODY_CHARS = 1000000;
26
+ /** What sync stores; a command that shows the files of one message passes its own limit. */
27
+ export declare const MAX_ATTACHMENTS = 50;
20
28
  export declare const htmlToText: (html: string) => string;
21
29
  /** Some senders put their HTML in the text/plain part; its first tag gives it away. */
22
30
  export declare const looksLikeHtml: (text: string) => boolean;
23
31
  /** A raw RFC 5322 message, as IMAP's `BODY[]` holds it. */
24
- export declare const parseMail: (raw: Uint8Array, maxBodyChars?: number) => Promise<ParsedMail>;
32
+ export declare const parseMail: (raw: Uint8Array, maxBodyChars?: number, maxAttachments?: number) => Promise<ParsedMail>;
@@ -2,6 +2,9 @@ import { CliError } from "@wirecat/cli-base";
2
2
  import PostalMime, {} from "postal-mime";
3
3
  // The store cuts a body into pieces itself; this only guards against a pathological one, like a pasted log.
4
4
  export const MAX_BODY_CHARS = 1_000_000;
5
+ /** What sync stores; a command that shows the files of one message passes its own limit. */
6
+ export const MAX_ATTACHMENTS = 50;
7
+ const BRACKETED_ID = /<[^<>\s]+>/g;
5
8
  const addresses = (list) => list
6
9
  .flatMap((entry) => entry.group ?? [entry])
7
10
  .flatMap(({ address, name }) => (address ? [{ email: address.toLowerCase(), name: name || null }] : []));
@@ -52,7 +55,7 @@ const bytesOf = (content) => typeof content === "string"
52
55
  ? content
53
56
  : new Uint8Array(content);
54
57
  /** A raw RFC 5322 message, as IMAP's `BODY[]` holds it. */
55
- export const parseMail = async (raw, maxBodyChars = MAX_BODY_CHARS) => {
58
+ export const parseMail = async (raw, maxBodyChars = MAX_BODY_CHARS, maxAttachments = MAX_ATTACHMENTS) => {
56
59
  let email;
57
60
  try {
58
61
  email = await PostalMime.parse(raw);
@@ -68,7 +71,11 @@ export const parseMail = async (raw, maxBodyChars = MAX_BODY_CHARS) => {
68
71
  to: addresses(email.to ?? []),
69
72
  cc: addresses(email.cc ?? []),
70
73
  bcc: addresses(email.bcc ?? []),
71
- attachments: email.attachments.slice(0, 50).map((part, id) => ({
74
+ replyTo: addresses(email.replyTo ?? []),
75
+ date: email.date ?? null,
76
+ messageId: email.messageId?.match(BRACKETED_ID)?.[0] ?? email.messageId ?? null,
77
+ references: email.references?.match(BRACKETED_ID) ?? [],
78
+ attachments: email.attachments.slice(0, maxAttachments).map((part, id) => ({
72
79
  id,
73
80
  name: (part.filename ?? `attachment-${id}`).slice(0, 200),
74
81
  mime: part.mimeType || null,
@@ -89,6 +89,8 @@ export interface MailSyncOptions {
89
89
  retryAttachments?: boolean;
90
90
  /** List every message in the window, not only those an earlier run left: proves deletions, refreshes labels. */
91
91
  rescan?: boolean;
92
+ /** `false` for a spot read, like the copy of a sent message: the run leaves the saved coverage as the last full run set it. */
93
+ saveCoverage?: boolean;
92
94
  /** A window smaller than this is listed whole every run: it costs seconds and keeps flags and deletions current. */
93
95
  resumeFrom?: number;
94
96
  /** Runs after the mail is saved, so chats and mail still waiting for vectors are indexed; its answer is `indexed`. */
package/dist/mail/sync.js CHANGED
@@ -141,7 +141,7 @@ export const syncMail = async (source, store, options) => {
141
141
  : {}),
142
142
  };
143
143
  };
144
- const syncRound = async (source, store, { account, since, max = 200, maxBodyChars = MAX_BODY_CHARS, maxAttachmentChars = MAX_ATTACHMENT_CHARS, now = Date.now, ages, loadEngine = importEngine, retryAttachments = false, rescan = false, resumeFrom = RESUME_FROM, index, signal, }, plan) => {
144
+ const syncRound = async (source, store, { account, since, max = 200, maxBodyChars = MAX_BODY_CHARS, maxAttachmentChars = MAX_ATTACHMENT_CHARS, now = Date.now, ages, loadEngine = importEngine, retryAttachments = false, rescan = false, saveCoverage = true, resumeFrom = RESUME_FROM, index, signal, }, plan) => {
145
145
  if (retryAttachments)
146
146
  return retryUnread({
147
147
  store,
@@ -777,7 +777,7 @@ const syncRound = async (source, store, { account, since, max = 200, maxBodyChar
777
777
  state: complete ? "window" : "partial",
778
778
  identity: account.mode === "imap" ? "message-id-or-uidvalidity-folder-uid" : "gmail-message-id",
779
779
  };
780
- if (!snapshot)
780
+ if (!snapshot && saveCoverage)
781
781
  await store.sync.setSyncState(key, "mail_coverage", JSON.stringify(coverage));
782
782
  const tagging = tagger.outcome();
783
783
  await settleRuleTags(store, accountId, rules, tagging);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wirecat/cli-core",
3
- "version": "0.533.0",
3
+ "version": "0.535.0",
4
4
  "description": "The messenger-neutral half of a messaging CLI: domain model, message locators, rendering, name resolution, the SQLite seam, the command skeleton and shared read commands, the send guard, run records and the message store",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Viacheslav Ptsarev",