proton-mail-bridge-client 2.13.0 → 2.14.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 (140) hide show
  1. package/README.md +44 -12
  2. package/dist/cli.d.ts +9 -0
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +61 -7
  5. package/dist/cli.js.map +1 -1
  6. package/dist/config-env.d.ts +21 -0
  7. package/dist/config-env.d.ts.map +1 -0
  8. package/dist/config-env.js +327 -0
  9. package/dist/config-env.js.map +1 -0
  10. package/dist/index.d.ts +17 -118
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +130 -7906
  13. package/dist/index.js.map +1 -1
  14. package/dist/package-info.d.ts +2 -0
  15. package/dist/package-info.d.ts.map +1 -0
  16. package/dist/package-info.js +6 -0
  17. package/dist/package-info.js.map +1 -0
  18. package/dist/services/account-manager.d.ts +5 -0
  19. package/dist/services/account-manager.d.ts.map +1 -1
  20. package/dist/services/account-manager.js +79 -0
  21. package/dist/services/account-manager.js.map +1 -1
  22. package/dist/services/autoresponder-service.d.ts +199 -0
  23. package/dist/services/autoresponder-service.d.ts.map +1 -0
  24. package/dist/services/autoresponder-service.js +569 -0
  25. package/dist/services/autoresponder-service.js.map +1 -0
  26. package/dist/services/background-sync-service.d.ts +4 -1
  27. package/dist/services/background-sync-service.d.ts.map +1 -1
  28. package/dist/services/background-sync-service.js +13 -0
  29. package/dist/services/background-sync-service.js.map +1 -1
  30. package/dist/services/local-index-service.d.ts.map +1 -1
  31. package/dist/services/local-index-service.js +2 -1
  32. package/dist/services/local-index-service.js.map +1 -1
  33. package/dist/services/simple-imap-service.d.ts +8 -1
  34. package/dist/services/simple-imap-service.d.ts.map +1 -1
  35. package/dist/services/simple-imap-service.js +52 -14
  36. package/dist/services/simple-imap-service.js.map +1 -1
  37. package/dist/services/smtp-service.d.ts +5 -0
  38. package/dist/services/smtp-service.d.ts.map +1 -1
  39. package/dist/services/smtp-service.js +20 -5
  40. package/dist/services/smtp-service.js.map +1 -1
  41. package/dist/tools/actions.d.ts +51 -0
  42. package/dist/tools/actions.d.ts.map +1 -0
  43. package/dist/tools/actions.js +323 -0
  44. package/dist/tools/actions.js.map +1 -0
  45. package/dist/tools/args.d.ts +22 -0
  46. package/dist/tools/args.d.ts.map +1 -0
  47. package/dist/tools/args.js +159 -0
  48. package/dist/tools/args.js.map +1 -0
  49. package/dist/tools/attachments.d.ts +4 -0
  50. package/dist/tools/attachments.d.ts.map +1 -0
  51. package/dist/tools/attachments.js +126 -0
  52. package/dist/tools/attachments.js.map +1 -0
  53. package/dist/tools/audit.d.ts +7 -0
  54. package/dist/tools/audit.d.ts.map +1 -0
  55. package/dist/tools/audit.js +112 -0
  56. package/dist/tools/audit.js.map +1 -0
  57. package/dist/tools/bulk.d.ts +18 -0
  58. package/dist/tools/bulk.d.ts.map +1 -0
  59. package/dist/tools/bulk.js +138 -0
  60. package/dist/tools/bulk.js.map +1 -0
  61. package/dist/tools/compose.d.ts +39 -0
  62. package/dist/tools/compose.d.ts.map +1 -0
  63. package/dist/tools/compose.js +300 -0
  64. package/dist/tools/compose.js.map +1 -0
  65. package/dist/tools/context.d.ts +78 -0
  66. package/dist/tools/context.d.ts.map +1 -0
  67. package/dist/tools/context.js +219 -0
  68. package/dist/tools/context.js.map +1 -0
  69. package/dist/tools/definitions.d.ts +3679 -0
  70. package/dist/tools/definitions.d.ts.map +1 -0
  71. package/dist/tools/definitions.js +1920 -0
  72. package/dist/tools/definitions.js.map +1 -0
  73. package/dist/tools/diagnostics.d.ts +4 -0
  74. package/dist/tools/diagnostics.d.ts.map +1 -0
  75. package/dist/tools/diagnostics.js +475 -0
  76. package/dist/tools/diagnostics.js.map +1 -0
  77. package/dist/tools/drafts-support.d.ts +46 -0
  78. package/dist/tools/drafts-support.d.ts.map +1 -0
  79. package/dist/tools/drafts-support.js +162 -0
  80. package/dist/tools/drafts-support.js.map +1 -0
  81. package/dist/tools/drafts.d.ts +4 -0
  82. package/dist/tools/drafts.d.ts.map +1 -0
  83. package/dist/tools/drafts.js +873 -0
  84. package/dist/tools/drafts.js.map +1 -0
  85. package/dist/tools/folders.d.ts +4 -0
  86. package/dist/tools/folders.d.ts.map +1 -0
  87. package/dist/tools/folders.js +194 -0
  88. package/dist/tools/folders.js.map +1 -0
  89. package/dist/tools/index-refresh.d.ts +54 -0
  90. package/dist/tools/index-refresh.d.ts.map +1 -0
  91. package/dist/tools/index-refresh.js +134 -0
  92. package/dist/tools/index-refresh.js.map +1 -0
  93. package/dist/tools/merge.d.ts +28 -0
  94. package/dist/tools/merge.d.ts.map +1 -0
  95. package/dist/tools/merge.js +207 -0
  96. package/dist/tools/merge.js.map +1 -0
  97. package/dist/tools/message-actions.d.ts +4 -0
  98. package/dist/tools/message-actions.d.ts.map +1 -0
  99. package/dist/tools/message-actions.js +636 -0
  100. package/dist/tools/message-actions.js.map +1 -0
  101. package/dist/tools/reading.d.ts +4 -0
  102. package/dist/tools/reading.d.ts.map +1 -0
  103. package/dist/tools/reading.js +918 -0
  104. package/dist/tools/reading.js.map +1 -0
  105. package/dist/tools/resources.d.ts +42 -0
  106. package/dist/tools/resources.d.ts.map +1 -0
  107. package/dist/tools/resources.js +182 -0
  108. package/dist/tools/resources.js.map +1 -0
  109. package/dist/tools/results.d.ts +25 -0
  110. package/dist/tools/results.d.ts.map +1 -0
  111. package/dist/tools/results.js +50 -0
  112. package/dist/tools/results.js.map +1 -0
  113. package/dist/tools/sending.d.ts +4 -0
  114. package/dist/tools/sending.d.ts.map +1 -0
  115. package/dist/tools/sending.js +624 -0
  116. package/dist/tools/sending.js.map +1 -0
  117. package/dist/tools/templates-and-reminders.d.ts +4 -0
  118. package/dist/tools/templates-and-reminders.d.ts.map +1 -0
  119. package/dist/tools/templates-and-reminders.js +249 -0
  120. package/dist/tools/templates-and-reminders.js.map +1 -0
  121. package/dist/types/index.d.ts +6 -0
  122. package/dist/types/index.d.ts.map +1 -1
  123. package/dist/utils/bridge-version.d.ts +8 -0
  124. package/dist/utils/bridge-version.d.ts.map +1 -0
  125. package/dist/utils/bridge-version.js +20 -0
  126. package/dist/utils/bridge-version.js.map +1 -0
  127. package/dist/utils/health.d.ts.map +1 -1
  128. package/dist/utils/health.js +7 -4
  129. package/dist/utils/health.js.map +1 -1
  130. package/dist/utils/helpers.d.ts.map +1 -1
  131. package/dist/utils/helpers.js +18 -5
  132. package/dist/utils/helpers.js.map +1 -1
  133. package/dist/utils/runtime-policy.d.ts.map +1 -1
  134. package/dist/utils/runtime-policy.js +2 -0
  135. package/dist/utils/runtime-policy.js.map +1 -1
  136. package/dist/utils/tls.d.ts +8 -0
  137. package/dist/utils/tls.d.ts.map +1 -0
  138. package/dist/utils/tls.js +28 -0
  139. package/dist/utils/tls.js.map +1 -0
  140. package/package.json +1 -1
package/README.md CHANGED
@@ -24,7 +24,7 @@
24
24
 
25
25
  ---
26
26
 
27
- Give Claude Desktop (or Cline, or any MCP client) full access to your Proton Mail inbox: read, search, send, draft, triage threads, manage folders, save attachments, and more — 100 MCP tools in total. Most of the same capabilities are also available as a full CLI for scripting, cron, and piped automation — no Claude required.
27
+ Give Claude Desktop (or Cline, or any MCP client) full access to your Proton Mail inbox: read, search, send, draft, triage threads, manage folders, save attachments, and more — 103 MCP tools in total. Most of the same capabilities are also available as a full CLI for scripting, cron, and piped automation — no Claude required.
28
28
 
29
29
  > **Using Proton Drive too?** See [**proton-drive-mcp**](https://github.com/googlarz/proton-drive-mcp), the companion MCP server and CLI for Proton Drive (upload, download, share and manage your encrypted files from Claude). Run both side by side to handle mail and files in one conversation, for example save an attachment from here, then upload it to Drive. More in [Related projects](#related-projects).
30
30
 
@@ -44,16 +44,19 @@ Give Claude Desktop (or Cline, or any MCP client) full access to your Proton Mai
44
44
  - "Find the last email from `alice@example.com` and draft a reply for me to review." (nothing is sent until you say so)
45
45
  - "Which of my emails have attachments from this month?"
46
46
  - "Show me my drafts and tighten the oldest one."
47
- 5. **Optional:** `PROTONMAIL_READ_ONLY=true` to browse without being able to change anything, `PROTONMAIL_TOOL_TIER=core` for a smaller tool set (25 instead of 100), and more accounts under [Multi-account support](#multi-account-support-v210). More recipes below in [Try it](#try-it-example-claude-prompts).
47
+ 5. **Optional:** `PROTONMAIL_READ_ONLY=true` to browse without being able to change anything, `PROTONMAIL_TOOL_TIER=core` for a smaller tool set (25 instead of 103), and more accounts under [Multi-account support](#multi-account-support-v210). More recipes below in [Try it](#try-it-example-claude-prompts).
48
48
 
49
49
  ---
50
50
 
51
51
  ## What you get
52
52
 
53
- - **Claude reads and manages your Proton Mail** — triage, reply, draft, archive, search, move, batch-act on threads, pull attachments
54
- - **Full CLI** — a dedicated command for every one of the 100 tools (plus a generic `tool <name>` passthrough), scriptable and pipeable, works in cron and shell scripts
55
- - **Fast local search** — full-text search across your inbox without hitting IMAP on every query
56
- - **Safety controls** — read-only mode, send gate, destructive-action confirmation, per-action allowlist
53
+ - **Claude reads and manages your Proton Mail** — triage, reply, draft, archive, search, move, batch-act on threads, pull attachments (PDF text and pictures included), answer calendar invitations
54
+ - **Safety you choose** — `PROTONMAIL_MODE` presets (`read-only`, `drafts`, `assisted`, `full`), a send gate, confirmation for destructive actions, a per-action allowlist, and a [threat model](docs/threat-model.md) that says what is and is not covered
55
+ - **Several addresses at once** — each Proton login (or each address Bridge exposes) is its own isolated account with its own index, drafts and settings
56
+ - **Fast local search** — full-text search across your inbox from a local index that keeps its read state in step with the server
57
+ - **Automatic reply, per address, with rules** — off by default, with a preview of what it would do, draft-for-approval mode and working-hours; see [Autoresponder](#autoresponder-automatic-reply-per-address) (Proton's own auto-reply may be all you need)
58
+ - **Full CLI** — a dedicated command for every one of the 103 tools (plus a generic `tool <name>` passthrough), scriptable and pipeable; `doctor` checks every account and `report` prints a diagnostic summary for a bug report
59
+ - **Runs where you do** — Claude Desktop bundle, npm, Homebrew, or a [Docker image](#running-in-docker-headless-machines) for a headless machine
57
60
  - **Privacy-native** — no third-party email service involved; your mail stays on your machine
58
61
 
59
62
  **How this relates to Proton Bridge.** This is a layer on top of [Proton Mail Bridge](https://proton.me/mail/bridge), not a replacement for it. Bridge is Proton's official, local IMAP/SMTP gateway and the only supported way to reach a Proton mailbox from other software; this project talks to it. Bridge's own `--cli` mode manages Bridge itself (signing in, account details and the Bridge password, ports and other settings), it is not a mail client. Proton does not publish a terminal mail client, so reading, searching, drafting and sending from Claude or a script is what this adds on top, together with the safety controls above. Bridge has to be running and signed in.
@@ -290,17 +293,44 @@ Reload the Cline extension after saving. Proton Mail tools will appear in Cline'
290
293
 
291
294
  ## Running in Docker (headless machines)
292
295
 
293
- For a server with no desktop, next to a headless Bridge. The image installs the published npm package; it is not published to a registry, so build it from this repo:
296
+ For a server with no desktop, next to a headless Bridge. The image installs the published npm package and is published for each release to `ghcr.io/googlarz/proton-mail-bridge-client` (`:<version>` and `:latest`, linux/amd64 and linux/arm64). To build it yourself from this repo:
294
297
 
295
298
  ```bash
296
- docker build -f docker/Dockerfile --build-arg VERSION=2.13.0 -t proton-mail-bridge-client .
299
+ docker build -f docker/Dockerfile --build-arg VERSION=2.14.0 -t proton-mail-bridge-client .
297
300
  docker run -i --rm --network host \
298
301
  -e PROTONMAIL_USERNAME=you@proton.me -e PROTONMAIL_PASSWORD_FILE=/run/secrets/bridge \
299
302
  -v /path/to/bridge-password:/run/secrets/bridge:ro -v bridge-client-data:/data \
300
303
  proton-mail-bridge-client
301
304
  ```
302
305
 
303
- `docker run -i` speaks MCP over stdio. The container must reach Bridge's IMAP and SMTP ports (`--network host`, or `--network container:<bridge>` to share Bridge's network). The CLI is in the same image: add `--entrypoint proton-mail-bridge-client` and a command such as `doctor`. The server has no network transport of its own; reaching it from a remote client needs your own wrapper. The image was checked to build, report its version, run as a non-root user and answer an MCP `initialize`; it has not been run against a real Bridge.
306
+ `docker run -i` speaks MCP over stdio. The container must reach Bridge's IMAP and SMTP ports (`--network host`, or `--network container:<bridge>` to share Bridge's network). The CLI is in the same image: add `--entrypoint proton-mail-bridge-client` and a command such as `doctor`. If Bridge is reached by a host name (for example `host.docker.internal` or another container), its self-signed certificate fails verification: export it from Bridge and point `PROTONMAIL_TLS_CA_FILE` at it, or list the name in `PROTONMAIL_TLS_INSECURE_HOSTS`. The server has no network transport of its own; reaching it from a remote client needs your own wrapper. The image was checked to build, report its version, run as a non-root user and answer an MCP `initialize`; it has not been run against a real Bridge.
307
+
308
+ ## Autoresponder (automatic reply per address)
309
+
310
+ **Proton Mail has its own auto-reply** (Settings → Filters / Forward and auto-reply; Proton lists it on its paid plans). It runs inside Proton, works while your computer is off, and applies to the whole account unless you write a filter. If that is enough, use it and leave this one off: enabling both would send each sender two replies. This one adds what a filter cannot do from a chat, and it only runs while this server runs.
311
+
312
+ An automatic reply for one address, plus a note that address appends to what it sends. **Off by default**, and it can send mail without asking each time, so it is held to stricter rules than a normal send:
313
+
314
+ - **Who gets a reply.** Only a person (not a list, not automatic mail, not a `no-reply`/`mailer-daemon` sender) who wrote to this address (To or Cc), after the rule was switched on and inside its dates, and whom you have not already answered since. One reply per sender per `replyEveryDays` days (default 7), at most `dailyLimit` a day (default 50). A reply is recorded before it is sent, so a failure never becomes a second one.
315
+ - **When.** After a background sync has indexed new mail, so only while this server runs with `PROTONMAIL_AUTO_SYNC` on; with several clients open, the instance holding the sync lock does it. `outsideHours` (with a `timezone`) limits it to when you are not at work.
316
+ - **What it does.** `mode: "send"` sends; `mode: "draft"` leaves a draft in your drafts for you to review and send. If the server may not send (`PROTONMAIL_MODE=drafts`, `ALLOW_SEND` off, or outbound mail confined to yourself), a reply becomes a draft by itself; if it may not write drafts either (read-only), it is refused.
317
+ - **Rules**, first match wins: by sender (`boss@example.com`, `*@client.example`), words in the subject, and whether you have written to the sender before (`known: "yes"/"no"`). Each gives `reply`, `draft` or `skip`, optionally with its own text. Mail that matches none gets the default reply.
318
+ - **Text.** Placeholders `{senderFirstName}`, `{senderName}`, `{senderAddress}`, `{subject}`, `{startsAt}`, `{endsAt}`.
319
+ - **Safety headers.** `Auto-Submitted: auto-replied`, `Precedence: bulk`, `X-Auto-Response-Suppress: All`; the reply threads under the message it answers.
320
+ - **Control.** Switching it on needs `confirmed: true` when `PROTONMAIL_CONFIRM_DESTRUCTIVE` is on and the server can send. Switching it off is always allowed, in every mode.
321
+
322
+ ```text
323
+ preview_autoresponder account=work@example.com days=3 # what it WOULD do with recent mail; changes nothing
324
+ set_autoresponder account=work@example.com enabled=true confirmed=true
325
+ body="Hi {senderFirstName}, I am away until {endsAt} and will answer then."
326
+ endsAt=2027-01-04 timezone=Europe/Warsaw outgoingNote="I am away until 4 January."
327
+ rules=[{"name":"boss","from":["boss@example.com"],"action":"reply","body":"Urgent? Call my mobile."},
328
+ {"name":"clients","from":["*@client.example"],"action":"draft"},
329
+ {"known":"no","subjectContains":["unsubscribe"],"action":"skip"}]
330
+ get_autoresponder account=work@example.com # state, counts, who wrote, why mail was skipped, the log
331
+ ```
332
+
333
+ `outgoingNote` is appended, after the signature, to every mail sent from that address while the rule is active (a send with `appendSignature: false` opts out). On the command line: `set-autoresponder`, `autoresponder` and `preview-autoresponder`. The rule, the log and the memory of who was answered live in `autoresponder.json` in the account's data directory; every account (every address Bridge exposes) has its own.
304
334
 
305
335
  ## Multi-account support (v2.1.0+)
306
336
 
@@ -441,7 +471,7 @@ All commands support `--json` for machine-readable output, and any MCP tool is d
441
471
 
442
472
  `<command> --help` prints that command's usage without running it. Flags accept `--flag value` or `--flag=value` (a value starting with `--` needs the `=` form, or must come after a bare `--`); unknown flags are an error. Exit codes: `0` success, `1` failure (including a failed `doctor` or `connection-status` check, or a failed item in a `batch`/`bulk-*` run), `2` usage error.
443
473
 
444
- **Full command reference: [docs/cli.md](docs/cli.md)** (a named command for every one of the 100 tools, across read, triage, compose, mailbox actions, folders, drafts, templates, analytics, and diagnostics).
474
+ **Full command reference: [docs/cli.md](docs/cli.md)** (a named command for every one of the 103 tools, across read, triage, compose, mailbox actions, folders, drafts, templates, analytics, and diagnostics).
445
475
 
446
476
  ---
447
477
 
@@ -463,7 +493,7 @@ A mode is a floor: the individual flags below can make things stricter (`PROTONM
463
493
  All flags work in both the MCP server and CLI:
464
494
 
465
495
  ```bash
466
- PROTONMAIL_TOOL_TIER=core # expose 25 core tools instead of all 100 — saves context window
496
+ PROTONMAIL_TOOL_TIER=core # expose 25 core tools instead of all 103 — saves context window
467
497
  PROTONMAIL_READ_ONLY=true # disable all write operations
468
498
  PROTONMAIL_ALLOW_SEND=false # disable SMTP sends only (other writes still work)
469
499
  PROTONMAIL_CONFIRM_DESTRUCTIVE=true # require confirmed:true on send, reply, forward, delete
@@ -538,6 +568,8 @@ PROTONMAIL_IDLE_WATCH='true'
538
568
  PROTONMAIL_IDLE_MAX_SECONDS='30'
539
569
 
540
570
  # Attachments
571
+ PROTONMAIL_TLS_CA_FILE='' # PEM file with Bridge's exported certificate: trusted when Bridge is reached by a name other than localhost (another container, another machine)
572
+ PROTONMAIL_TLS_INSECURE_HOSTS='' # comma-separated host names whose certificate is not verified, like localhost is (only for a private network you trust)
541
573
  PROTONMAIL_SHORTEN_LINKS='false' # true = long tracking links in Markdown bodies become host/… (unsubscribe, confirm, reset links are kept)
542
574
  PROTONMAIL_MAX_INLINE_KB='40' # in KB (older name PROTONMAIL_MAX_INLINE_BYTES still works, also in KB): largest attachment get_attachment_content returns inline as base64
543
575
  PROTONMAIL_PDF_MAX_MB='10' # largest PDF get_attachment_text extracts text from (1-100 MB); bigger ones are refused with a message
@@ -684,7 +716,7 @@ Measured in October 2026 (v2.7.0) on a copy of the same ~57,000-message index. "
684
716
  - Defaults are bounded: `search_emails`/`search_indexed_emails` return 50 (pass `limit` for more), `list_drafts` 20, `list_scheduled_sends` 50 newest first. Each reports `hasMore`; page with `offset`.
685
717
  - Draft and queued-message bodies are shortened to a preview in list and update responses (`bodyTruncated`, `bodyLength`); use `get_draft` for one full draft. Attachment content is never inlined in a listing.
686
718
  - Large attachments: set `PROTONMAIL_ALLOW_FILE_DOWNLOAD_DIR` and pass `saveTo`, rather than raising `PROTONMAIL_MAX_INLINE_KB`.
687
- - **`PROTONMAIL_TOOL_TIER=core`** exposes 25 tools instead of 100: about 7k tokens of tool definitions instead of about 21k (roughly 67% less; the earlier measurement with a reference tokenizer, 7k against 20k for 96 tools, scaled by the 3.7% the four newest tool definitions add), paid on every conversation that loads the server.
719
+ - **`PROTONMAIL_TOOL_TIER=core`** exposes 25 tools instead of 103: about 7k tokens of tool definitions instead of about 21k (roughly 67% less; the earlier measurement with a reference tokenizer, 7k against 20k for 96 tools, scaled by the 3.7% the four newest tool definitions add), paid on every conversation that loads the server.
688
720
  - Repeating an `update_draft` that changes nothing on a draft whose remote copy is already in sync does no store write and no IMAP upload. (A draft whose last sync failed, or that was edited with `syncToRemote:false`, still syncs.)
689
721
 
690
722
  **Working with drafts**
package/dist/cli.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import type { ProtonMailConfig } from "./types/index.js";
2
3
  type CliFlags = Record<string, string | boolean>;
3
4
  export interface ParsedCliArgs {
4
5
  command: string;
@@ -29,6 +30,14 @@ export declare function diagnoseConnectionFailure(reason: unknown): {
29
30
  suggestion: string;
30
31
  } | undefined;
31
32
  export declare function maskAddress(address: string): string;
33
+ export declare function reportRuntime(runtime: ProtonMailConfig["runtime"], home?: string): Record<string, unknown>;
34
+ export declare function watchHealthNotice(previous: string | undefined, error: unknown): {
35
+ cause: string | undefined;
36
+ notice?: {
37
+ title: string;
38
+ body: string;
39
+ };
40
+ };
32
41
  interface ToolOnlyCommand {
33
42
  command: string;
34
43
  tool: string;
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAgCA,KAAK,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;AAEjD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,EAAE,QAAQ,CAAC;CACjB;AAiBD,qBAAa,aAAc,SAAQ,KAAK;CAAG;AAM3C,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CAuD1D;AA0ID,UAAU,WAAW;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAOD,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAqErD,CAAC;AAoBF,eAAO,MAAM,iBAAiB,YAAI,KAAK,EAAE,MAAM,EAAE,MAAM,CAAU,CAAC;AAGlE,wBAAgB,kBAAkB,IAAI,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAQlG;AAID,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CA+DtD;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAcnE;AAyKD,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAY5F;AAiND,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,OAAO,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAiB5G;AA+FD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGnD;AAwmCD,UAAU,eAAe;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IAIb,SAAS,CAAC,EAAE,MAAM,CAAC;IAMnB,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,eAAO,MAAM,kBAAkB,EAAE,eAAe,EAmE/C,CAAC;AA2EF,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAoP1C;AA4ED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAGzD"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAaA,OAAO,KAAK,EAAgB,gBAAgB,EAAqB,MAAM,kBAAkB,CAAC;AAqB1F,KAAK,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;AAEjD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,EAAE,QAAQ,CAAC;CACjB;AAiBD,qBAAa,aAAc,SAAQ,KAAK;CAAG;AAM3C,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CAuD1D;AA0ID,UAAU,WAAW;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAOD,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAqErD,CAAC;AAoBF,eAAO,MAAM,iBAAiB,YAAI,KAAK,EAAE,MAAM,EAAE,MAAM,CAAU,CAAC;AAGlE,wBAAgB,kBAAkB,IAAI,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAQlG;AAID,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CA+DtD;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAcnE;AAyKD,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAY5F;AAiND,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,OAAO,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAiB5G;AAgGD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGnD;AAGD,wBAAgB,aAAa,CAAC,OAAO,EAAE,gBAAgB,CAAC,SAAS,CAAC,EAAE,IAAI,SAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAS7G;AA01BD,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,KAAK,EAAE,OAAO,GACb;IAAE,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAYzE;AAySD,UAAU,eAAe;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IAIb,SAAS,CAAC,EAAE,MAAM,CAAC;IAMnB,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,eAAO,MAAM,kBAAkB,EAAE,eAAe,EAsE/C,CAAC;AA2EF,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAoP1C;AA4ED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAGzD"}
package/dist/cli.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import { execFile } from "node:child_process";
3
3
  import { accessSync, constants as fsConstants, realpathSync } from "node:fs";
4
4
  import { readFile } from "node:fs/promises";
5
+ import { homedir, release as osRelease } from "node:os";
5
6
  import { promisify } from "node:util";
6
7
  import { fileURLToPath } from "node:url";
7
8
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
@@ -13,6 +14,7 @@ import { ensureDestructiveConfirmed, ensureEmailActionAllowed, ensureMailboxWrit
13
14
  import { isValidEmail, parseEmails } from "./utils/helpers.js";
14
15
  import { checkForUpdate } from "./utils/update-check.js";
15
16
  import { healthProblems } from "./utils/health.js";
17
+ import { bridgeVersionNote } from "./utils/bridge-version.js";
16
18
  import { buildSchedule, defaultSchedulerSystem, findStableAlias, parseTimeOfDay, SCHEDULER_SYSTEMS } from "./utils/schedule-templates.js";
17
19
  import { getClaudeDesktopInstallStatus } from "./scripts/check-claude-desktop.js";
18
20
  import { installClaudeDesktopConfig } from "./scripts/install-claude-desktop.js";
@@ -284,7 +286,7 @@ export const COMMAND_SPECS = {
284
286
  watch: { usage: "watch [--folder <name>] [--timeout <s>]", description: "Wait for mailbox changes via IMAP IDLE", flags: ["folder", "timeout"] },
285
287
  "clear-cache": { usage: "clear-cache", description: "Clear in-memory MCP server caches", flags: [] },
286
288
  "get-logs": { usage: "get-logs [--limit <n>] [--offset <n>] [--level <level>]", description: "Return recent in-memory MCP server logs", flags: ["limit", "level", "offset"] },
287
- notify: { usage: "notify [--folder <name>] [--timeout <s>]", description: "Daemon: watch a folder and send a system notification on new mail", flags: ["folder", "timeout"] },
289
+ notify: { usage: "notify [--folder <name>] [--timeout <s>]", description: "Daemon: watch a folder and send a system notification on new mail, and when it cannot reach or log in to Bridge (once per problem, and when it recovers)", flags: ["folder", "timeout"] },
288
290
  drafts: { usage: "drafts [--sent]", description: "List local drafts", flags: ["sent"] },
289
291
  "remote-drafts": { usage: "remote-drafts [--limit <n>] [--offset <n>]", description: "List drafts in the Proton Drafts mailbox", flags: ["limit", "offset"] },
290
292
  "draft-create": { usage: "draft-create --subject <text> [--to <addr>] (--body <text> | stdin) [--cc <a>] [--bcc <a>]", description: "Create a draft", flags: ["to", "cc", "bcc", "subject", ...BODY_FLAGS] },
@@ -800,6 +802,7 @@ async function runDoctor(parsed) {
800
802
  slug: bundle.account.slug,
801
803
  account: bundle.config.smtp.username,
802
804
  ok: imapOk && smtpOk,
805
+ ...(bridgeVersionNote(bundle.imapService.getServerInfo()) ? { bridgeNote: bridgeVersionNote(bundle.imapService.getServerInfo()) } : {}),
803
806
  imapOk,
804
807
  smtpOk,
805
808
  folderCount,
@@ -868,6 +871,17 @@ export function maskAddress(address) {
868
871
  const at = address.lastIndexOf("@");
869
872
  return at <= 0 ? "***" : `${address[0]}***${address.slice(at)}`;
870
873
  }
874
+ // The runtime settings for a bug report: folder lists become a count, and the home directory is written as ~.
875
+ export function reportRuntime(runtime, home = homedir()) {
876
+ const sanitized = sanitizeRuntimeConfig(runtime);
877
+ const folders = typeof sanitized.autoSyncFolder === "string" ? sanitized.autoSyncFolder.split(",").filter((entry) => entry.trim()).length : undefined;
878
+ const download = typeof sanitized.allowFileDownloadDir === "string" ? sanitized.allowFileDownloadDir : undefined;
879
+ return {
880
+ ...sanitized,
881
+ autoSyncFolder: folders === undefined ? undefined : `${folders} folder${folders === 1 ? "" : "s"}`,
882
+ allowFileDownloadDir: download ? (home && download.startsWith(home) ? `~${download.slice(home.length)}` : download) : sanitized.allowFileDownloadDir,
883
+ };
884
+ }
871
885
  async function runReport() {
872
886
  await withServices(async ({ config, accountManager }) => {
873
887
  const accounts = await Promise.all(accountManager.all().map(async (bundle) => {
@@ -891,19 +905,28 @@ async function runReport() {
891
905
  return {
892
906
  account: maskAddress(bundle.account.address),
893
907
  imap: { host: bundle.config.imap.host, port: bundle.config.imap.port, secure: bundle.config.imap.secure, check: imap },
894
- smtp: { host: bundle.config.smtp.host, port: bundle.config.smtp.port, secure: bundle.config.smtp.secure, userSet: bundle.config.smtp.secureExplicit === true, check: smtp },
908
+ smtp: {
909
+ host: bundle.config.smtp.host,
910
+ port: bundle.config.smtp.port,
911
+ configuredSecure: bundle.config.smtp.secure,
912
+ secureInUse: bundle.smtpService.effectiveSecure,
913
+ userSet: bundle.config.smtp.secureExplicit === true,
914
+ check: smtp,
915
+ },
916
+ bridge: { ...bundle.imapService.getServerInfo(), note: bridgeVersionNote(bundle.imapService.getServerInfo()) },
895
917
  folderCount,
896
918
  index: { storedMessageCount: index.storedMessageCount, updatedAt: index.updatedAt, isStale: index.isStale },
897
- backgroundSync: { folder: bundle.backgroundSyncService.getStatus().folder, full: bundle.backgroundSyncService.getStatus().full },
919
+ // Folder names can be as private as an address: only how many are synced.
920
+ backgroundSync: { syncedFolderCount: bundle.imapService.getSyncFolderList().length, full: bundle.backgroundSyncService.getStatus().full },
898
921
  };
899
922
  }));
900
923
  const claudeDesktop = await getClaudeDesktopInstallStatus();
901
924
  process.stdout.write(json({
902
925
  version: await getPkgVersion(),
903
926
  node: process.version,
904
- platform: `${process.platform} ${process.arch}`,
927
+ platform: `${process.platform} ${process.arch} ${osRelease()}`,
905
928
  claudeDesktopInstalled: claudeDesktop.installed,
906
- runtime: sanitizeRuntimeConfig(config.runtime),
929
+ runtime: reportRuntime(config.runtime),
907
930
  accounts,
908
931
  }));
909
932
  });
@@ -1649,6 +1672,21 @@ async function sendSystemNotification(title, body) {
1649
1672
  // Silent — stdout already carries the event
1650
1673
  }
1651
1674
  }
1675
+ // What `notify` should tell the user when its own watch starts or stops working. A problem is announced once per
1676
+ // cause (not every retry), and recovery once; `cause` is what the caller passes back in as `previous`.
1677
+ export function watchHealthNotice(previous, error) {
1678
+ if (error === null) {
1679
+ return previous
1680
+ ? { cause: undefined, notice: { title: "Proton Mail Bridge Client", body: "The connection to Bridge works again." } }
1681
+ : { cause: undefined };
1682
+ }
1683
+ const cause = diagnoseConnectionFailure(error)?.cause ?? "failed";
1684
+ if (cause === previous)
1685
+ return { cause };
1686
+ const body = diagnoseConnectionFailure(error)?.suggestion ??
1687
+ `The mailbox watch stopped working: ${error instanceof Error ? error.message : String(error)}`;
1688
+ return { cause, notice: { title: "Proton Mail Bridge Client needs attention", body } };
1689
+ }
1652
1690
  async function runNotify(parsed) {
1653
1691
  const folder = getStringFlag(parsed.flags, "folder") || "INBOX";
1654
1692
  const timeoutSeconds = getNumberFlag(parsed.flags, "timeout", 30);
@@ -1656,6 +1694,7 @@ async function runNotify(parsed) {
1656
1694
  const imapService = new SimpleIMAPService(config);
1657
1695
  let running = true;
1658
1696
  let previousCount;
1697
+ let problemCause;
1659
1698
  const shutdown = () => { running = false; };
1660
1699
  process.on("SIGINT", shutdown);
1661
1700
  process.on("SIGTERM", shutdown);
@@ -1668,6 +1707,12 @@ async function runNotify(parsed) {
1668
1707
  });
1669
1708
  if (!running)
1670
1709
  break;
1710
+ const recovered = watchHealthNotice(problemCause, null);
1711
+ if (recovered.notice) {
1712
+ await sendSystemNotification(recovered.notice.title, recovered.notice.body);
1713
+ process.stdout.write(JSON.stringify({ event: "recovered", at: result.checkedAt }) + "\n");
1714
+ }
1715
+ problemCause = recovered.cause;
1671
1716
  if (result.changed) {
1672
1717
  const existsEvent = result.events.find((e) => e["type"] === "exists");
1673
1718
  if (existsEvent) {
@@ -1694,8 +1739,14 @@ async function runNotify(parsed) {
1694
1739
  break;
1695
1740
  const msg = error instanceof Error ? error.message : String(error);
1696
1741
  process.stderr.write(`[notify] Watch error: ${msg}\n`);
1697
- // Simple backoff before retrying
1698
- await new Promise((resolve) => { setTimeout(resolve, 15_000).unref(); });
1742
+ const health = watchHealthNotice(problemCause, error);
1743
+ problemCause = health.cause;
1744
+ if (health.notice) {
1745
+ await sendSystemNotification(health.notice.title, health.notice.body);
1746
+ process.stdout.write(JSON.stringify({ event: "problem", cause: health.cause, message: health.notice.body }) + "\n");
1747
+ }
1748
+ // Simple backoff before retrying; a rejected login will not fix itself in seconds.
1749
+ await new Promise((resolve) => { setTimeout(resolve, health.cause === "authentication_failed" ? 120_000 : 15_000).unref(); });
1699
1750
  }
1700
1751
  }
1701
1752
  try {
@@ -1943,6 +1994,9 @@ export const TOOL_ONLY_COMMANDS = [
1943
1994
  { command: "cancel-snooze", tool: "cancel_snooze", positionals: ["id"], help: "Wake a snoozed email immediately" },
1944
1995
  { command: "list-snoozed", tool: "list_snoozed", positionals: [], help: "List snoozed emails (rediscover an id for cancel-snooze)" },
1945
1996
  { command: "create-template", tool: "create_template", positionals: ["name", "subject", "body"], help: "Save a reusable email template" },
1997
+ { command: "preview-autoresponder", tool: "preview_autoresponder", positionals: [], help: "Show what the automatic reply would do with recent mail, without doing it (--account, --days)" },
1998
+ { command: "autoresponder", tool: "get_autoresponder", positionals: [], help: "Show the automatic reply of an address (--account)" },
1999
+ { command: "set-autoresponder", tool: "set_autoresponder", positionals: [], help: "Set up or switch off the automatic reply of an address (--enabled, --body, --subject, --startsAt, --endsAt, --outgoingNote, --mode, --timezone, --rules, --confirmed)" },
1946
2000
  { command: "list-templates", tool: "list_templates", positionals: [], help: "List saved email templates" },
1947
2001
  { command: "get-template", tool: "get_template", positionals: ["id"], help: "Get a saved template by id" },
1948
2002
  { command: "delete-template", tool: "delete_template", positionals: ["id"], help: "Delete a saved template" },