proton-mail-bridge-client 2.11.0 → 2.13.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/README.md +16 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +155 -42
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +65 -21
- package/dist/index.js.map +1 -1
- package/dist/services/background-sync-service.d.ts +1 -0
- package/dist/services/background-sync-service.d.ts.map +1 -1
- package/dist/services/background-sync-service.js +26 -0
- package/dist/services/background-sync-service.js.map +1 -1
- package/dist/services/local-index-service.d.ts.map +1 -1
- package/dist/services/local-index-service.js +55 -1
- package/dist/services/local-index-service.js.map +1 -1
- package/dist/services/simple-imap-service.d.ts +4 -0
- package/dist/services/simple-imap-service.d.ts.map +1 -1
- package/dist/services/simple-imap-service.js +64 -5
- package/dist/services/simple-imap-service.js.map +1 -1
- package/dist/services/sync-leader.d.ts +19 -0
- package/dist/services/sync-leader.d.ts.map +1 -0
- package/dist/services/sync-leader.js +128 -0
- package/dist/services/sync-leader.js.map +1 -0
- package/dist/types/index.d.ts +14 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/health.d.ts +11 -0
- package/dist/utils/health.d.ts.map +1 -0
- package/dist/utils/health.js +38 -0
- package/dist/utils/health.js.map +1 -0
- package/dist/utils/helpers.d.ts +1 -0
- package/dist/utils/helpers.d.ts.map +1 -1
- package/dist/utils/helpers.js +31 -6
- package/dist/utils/helpers.js.map +1 -1
- package/dist/utils/pdf-text-worker.d.ts +2 -0
- package/dist/utils/pdf-text-worker.d.ts.map +1 -0
- package/dist/utils/pdf-text-worker.js +17 -0
- package/dist/utils/pdf-text-worker.js.map +1 -0
- package/dist/utils/pdf-text.d.ts +2 -0
- package/dist/utils/pdf-text.d.ts.map +1 -1
- package/dist/utils/pdf-text.js +28 -2
- package/dist/utils/pdf-text.js.map +1 -1
- package/dist/utils/runtime-policy.d.ts.map +1 -1
- package/dist/utils/runtime-policy.js +1 -0
- package/dist/utils/runtime-policy.js.map +1 -1
- package/dist/utils/update-check.d.ts +10 -0
- package/dist/utils/update-check.d.ts.map +1 -0
- package/dist/utils/update-check.js +37 -0
- package/dist/utils/update-check.js.map +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -288,6 +288,20 @@ Reload the Cline extension after saving. Proton Mail tools will appear in Cline'
|
|
|
288
288
|
|
|
289
289
|
---
|
|
290
290
|
|
|
291
|
+
## Running in Docker (headless machines)
|
|
292
|
+
|
|
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:
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
docker build -f docker/Dockerfile --build-arg VERSION=2.13.0 -t proton-mail-bridge-client .
|
|
297
|
+
docker run -i --rm --network host \
|
|
298
|
+
-e PROTONMAIL_USERNAME=you@proton.me -e PROTONMAIL_PASSWORD_FILE=/run/secrets/bridge \
|
|
299
|
+
-v /path/to/bridge-password:/run/secrets/bridge:ro -v bridge-client-data:/data \
|
|
300
|
+
proton-mail-bridge-client
|
|
301
|
+
```
|
|
302
|
+
|
|
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.
|
|
304
|
+
|
|
291
305
|
## Multi-account support (v2.1.0+)
|
|
292
306
|
|
|
293
307
|
Use more than one Proton mailbox from the same server: either several addresses of one account (Bridge's **Split Addresses** feature) or separate Proton accounts signed in to the same Bridge. Each one gets its own isolated service stack, local index, and drafts storage.
|
|
@@ -524,6 +538,7 @@ PROTONMAIL_IDLE_WATCH='true'
|
|
|
524
538
|
PROTONMAIL_IDLE_MAX_SECONDS='30'
|
|
525
539
|
|
|
526
540
|
# Attachments
|
|
541
|
+
PROTONMAIL_SHORTEN_LINKS='false' # true = long tracking links in Markdown bodies become host/… (unsubscribe, confirm, reset links are kept)
|
|
527
542
|
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
|
|
528
543
|
PROTONMAIL_PDF_MAX_MB='10' # largest PDF get_attachment_text extracts text from (1-100 MB); bigger ones are refused with a message
|
|
529
544
|
PROTONMAIL_PDF_MAX_PAGES='50' # pages get_attachment_text reads from a PDF (1-2000); a longer PDF is cut off with a note saying how many pages are missing
|
|
@@ -695,6 +710,7 @@ To check a real Bridge yourself, run the read-only smoke test: `PROTONMAIL_USERN
|
|
|
695
710
|
- `get_emails` and `search_emails` return a composite `emailId` — use it for all subsequent reads and actions.
|
|
696
711
|
- `search_indexed_emails` supports `from:`, `to:`, `subject:`, `label:`, `domain:` shortcuts.
|
|
697
712
|
- The local index lives at `PROTONMAIL_DATA_DIR/mail-index.sqlite`. Background sync and IMAP IDLE keep it warm.
|
|
713
|
+
- **Several clients at once.** Every MCP client starts its own server. Per data directory, only one of them runs the background sync and the IDLE watch (it holds `background-sync.lock`); the others serve tools from the index and take over when it exits. `get_runtime_status` shows `backgroundSync.leader`. A manual `sync_emails` / `run_background_sync` always runs.
|
|
698
714
|
- Filters on the local index ignore case and accents, and match spelling variants: `Müller` finds `Mueller`, `lodz` finds `Łódź`, `strasse` finds `Straße`. Live `search_emails` and `count_messages` go through Bridge: a query with umlauts or other non-ASCII letters also matches the spelled-out form (`Müller` finds `Mueller`), but a plain ASCII query is sent as typed, so `Mueller` does not find `Müller` there. Use `search_indexed_emails` for that.
|
|
699
715
|
- Audit logs live at `PROTONMAIL_DATA_DIR/audit.log`.
|
|
700
716
|
- Draft sync is best-effort — the local draft is always preserved even if remote sync fails.
|
package/dist/cli.d.ts
CHANGED
|
@@ -24,6 +24,11 @@ export declare function completionCommands(): Array<{
|
|
|
24
24
|
export declare function completionScript(shell: string): string;
|
|
25
25
|
export declare function commandHelpText(command: string): string | undefined;
|
|
26
26
|
export declare function toolResultExitCode(toolName: string, result: Record<string, unknown>): number;
|
|
27
|
+
export declare function diagnoseConnectionFailure(reason: unknown): {
|
|
28
|
+
cause: string;
|
|
29
|
+
suggestion: string;
|
|
30
|
+
} | undefined;
|
|
31
|
+
export declare function maskAddress(address: string): string;
|
|
27
32
|
interface ToolOnlyCommand {
|
|
28
33
|
command: string;
|
|
29
34
|
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":";
|
|
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"}
|
package/dist/cli.js
CHANGED
|
@@ -8,9 +8,11 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
|
8
8
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
9
9
|
import { buildConfigFromEnv, createServer, withAudit } from "./index.js";
|
|
10
10
|
import { isMainModule } from "./is-main.js";
|
|
11
|
-
import { SimpleIMAPService } from "./services/simple-imap-service.js";
|
|
11
|
+
import { isLikelyAuthenticationError, isLikelyConnectionError, isLikelyTlsMismatchError, SimpleIMAPService } from "./services/simple-imap-service.js";
|
|
12
12
|
import { ensureDestructiveConfirmed, ensureEmailActionAllowed, ensureMailboxWriteAllowed, sanitizeRuntimeConfig } from "./utils/runtime-policy.js";
|
|
13
13
|
import { isValidEmail, parseEmails } from "./utils/helpers.js";
|
|
14
|
+
import { checkForUpdate } from "./utils/update-check.js";
|
|
15
|
+
import { healthProblems } from "./utils/health.js";
|
|
14
16
|
import { buildSchedule, defaultSchedulerSystem, findStableAlias, parseTimeOfDay, SCHEDULER_SYSTEMS } from "./utils/schedule-templates.js";
|
|
15
17
|
import { getClaudeDesktopInstallStatus } from "./scripts/check-claude-desktop.js";
|
|
16
18
|
import { installClaudeDesktopConfig } from "./scripts/install-claude-desktop.js";
|
|
@@ -231,13 +233,14 @@ export const COMMAND_SPECS = {
|
|
|
231
233
|
completion: { usage: "completion <zsh|bash|fish>", description: "Print a shell completion script for the commands and their flags", flags: [] },
|
|
232
234
|
schedule: { usage: "schedule [--at HH:MM] [--system launchd|systemd|cron|schtasks] [--output <file>] [--password-file <file>]", description: "Print the scheduler definition that runs `digest` every day, with the commands to switch it on (installs nothing)", flags: ["at", "system", "output", "password-file"] },
|
|
233
235
|
help: { usage: "help [command]", description: "Show the command list, or the help for one command", flags: [] },
|
|
234
|
-
version: { usage: "version", description: "Print the version and exit", flags: [] },
|
|
236
|
+
version: { usage: "version [--check]", description: "Print the version and exit; --check also asks the npm registry whether a newer release exists", flags: ["check"] },
|
|
235
237
|
"setup-claude-desktop": { usage: "setup-claude-desktop", description: "Run the Claude Desktop setup wizard (works from any install)", flags: [] },
|
|
236
238
|
claude: { usage: "claude <setup|install|check|update|doctor>", description: "Claude Desktop integration: setup wizard, install or update the runtime, check status (update is an alias for install, doctor for check)", flags: [] },
|
|
237
239
|
status: { usage: "status", description: "Show local config, index, runtime, and Claude Desktop status", flags: [] },
|
|
238
|
-
doctor: { usage: "doctor", description: "Verify IMAP, SMTP, and Claude Desktop wiring (exits 1 when the check fails)", flags: [] },
|
|
240
|
+
doctor: { usage: "doctor [--account <slug|address>] [--check-updates]", description: "Verify IMAP, SMTP, and Claude Desktop wiring for every account, or one (exits 1 when the check fails); --check-updates also asks the npm registry for a newer release", flags: ["account", "check-updates"] },
|
|
239
241
|
"connection-status": { usage: "connection-status", description: "Show live IMAP/SMTP connectivity state (exits 1 when either is unreachable)", flags: [] },
|
|
240
|
-
|
|
242
|
+
report: { usage: "report", description: "Print a diagnostic summary to paste into a bug report: versions, system, connection modes, flags and per-account state; no passwords, no mail content, addresses masked", flags: [] },
|
|
243
|
+
"runtime-status": { usage: "runtime-status [--account <slug|address>]", description: "Show runtime policy and background sync state, per account", flags: ["account"] },
|
|
241
244
|
sync: { usage: "sync [--folder <name>] [--limit <n>] [--full]", description: "Refresh the local index from Proton Bridge", flags: ["folder", "limit", "full", "no-attachment-text"] },
|
|
242
245
|
"index-status": { usage: "index-status", description: "Show local index health and freshness", flags: [] },
|
|
243
246
|
folders: { usage: "folders", description: "List available folders from Proton Bridge", flags: [] },
|
|
@@ -655,9 +658,22 @@ async function withServices(run) {
|
|
|
655
658
|
return await run({ config, ...services });
|
|
656
659
|
}
|
|
657
660
|
finally {
|
|
658
|
-
services.
|
|
659
|
-
|
|
661
|
+
for (const bundle of services.accountManager.all()) {
|
|
662
|
+
bundle.backgroundSyncService.stop();
|
|
663
|
+
await Promise.allSettled([bundle.imapService.disconnect(), bundle.smtpService.close()]);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
// --account picks one account (slug or address); without it, every configured account.
|
|
668
|
+
function selectAccountBundles(accountManager, parsed) {
|
|
669
|
+
const wanted = getStringFlag(parsed.flags, "account");
|
|
670
|
+
if (!wanted)
|
|
671
|
+
return accountManager.all();
|
|
672
|
+
const bundle = accountManager.byAddress(wanted) ?? accountManager.all().find((entry) => entry.account.slug === wanted);
|
|
673
|
+
if (!bundle) {
|
|
674
|
+
throw new CliUsageError(`Unknown account "${wanted}". Configured: ${accountManager.all().map((entry) => entry.account.slug).join(", ")}`);
|
|
660
675
|
}
|
|
676
|
+
return [bundle];
|
|
661
677
|
}
|
|
662
678
|
async function syncIndex(context, input) {
|
|
663
679
|
const snapshot = await context.imapService.collectEmailsForIndex({
|
|
@@ -740,41 +756,72 @@ async function runStatus(parsed) {
|
|
|
740
756
|
process.stdout.write(wantJson ? json(result) : renderStatus(result));
|
|
741
757
|
});
|
|
742
758
|
}
|
|
759
|
+
// Same classification as the run_doctor tool, so the CLI explains a failure instead of printing the raw error.
|
|
760
|
+
export function diagnoseConnectionFailure(reason) {
|
|
761
|
+
if (isLikelyAuthenticationError(reason)) {
|
|
762
|
+
return {
|
|
763
|
+
cause: "authentication_failed",
|
|
764
|
+
suggestion: "PROTONMAIL_PASSWORD must be the Bridge password (Bridge app -> account -> Mailbox details), not your Proton account password.",
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
if (isLikelyTlsMismatchError(reason)) {
|
|
768
|
+
return {
|
|
769
|
+
cause: "tls_mismatch",
|
|
770
|
+
suggestion: "TLS handshake failed: PROTONMAIL_IMAP_SECURE / PROTONMAIL_SMTP_SECURE probably does not match the connection mode of that port in Bridge (implicit TLS vs STARTTLS).",
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
if (isLikelyConnectionError(reason)) {
|
|
774
|
+
return { cause: "bridge_unreachable", suggestion: "Bridge is not reachable: check that it is running and that the host and ports match Bridge's Mailbox details." };
|
|
775
|
+
}
|
|
776
|
+
return undefined;
|
|
777
|
+
}
|
|
743
778
|
async function runDoctor(parsed) {
|
|
744
779
|
const wantJson = isTruthyFlag(parsed.flags.json);
|
|
745
|
-
await withServices(async ({
|
|
746
|
-
const
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
780
|
+
await withServices(async ({ accountManager }) => {
|
|
781
|
+
const bundles = selectAccountBundles(accountManager, parsed);
|
|
782
|
+
const checked = await Promise.all(bundles.map(async (bundle) => {
|
|
783
|
+
let imapOk = false;
|
|
784
|
+
let smtpOk = false;
|
|
785
|
+
let folderCount = 0;
|
|
786
|
+
let error;
|
|
787
|
+
let diagnosis;
|
|
788
|
+
try {
|
|
789
|
+
await bundle.imapService.ping();
|
|
790
|
+
imapOk = true;
|
|
791
|
+
folderCount = (await bundle.imapService.getFolders(true)).length;
|
|
792
|
+
await bundle.smtpService.verifyConnection();
|
|
793
|
+
smtpOk = true;
|
|
794
|
+
}
|
|
795
|
+
catch (err) {
|
|
796
|
+
error = err instanceof Error ? err.message : String(err);
|
|
797
|
+
diagnosis = diagnoseConnectionFailure(err);
|
|
798
|
+
}
|
|
799
|
+
return {
|
|
800
|
+
slug: bundle.account.slug,
|
|
801
|
+
account: bundle.config.smtp.username,
|
|
802
|
+
ok: imapOk && smtpOk,
|
|
803
|
+
imapOk,
|
|
804
|
+
smtpOk,
|
|
805
|
+
folderCount,
|
|
806
|
+
indexUpdatedAt: (await bundle.localIndexService.getStatus()).updatedAt,
|
|
807
|
+
error,
|
|
808
|
+
...(diagnosis ? { cause: diagnosis.cause, suggestion: diagnosis.suggestion } : {}),
|
|
809
|
+
};
|
|
810
|
+
}));
|
|
811
|
+
const claudeDesktop = await getClaudeDesktopInstallStatus();
|
|
812
|
+
const { slug: _slug, ...firstFields } = checked[0];
|
|
764
813
|
const result = {
|
|
765
|
-
ok: imapOk && smtpOk,
|
|
766
814
|
// A stale/orphaned install elsewhere on disk (wrong path, old
|
|
767
815
|
// version) looks identical to a fresh one on every other field
|
|
768
816
|
// above — this is the field that actually distinguishes them.
|
|
769
817
|
version: await getPkgVersion(),
|
|
770
818
|
entrypoint: cliEntryPath(),
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
folderCount,
|
|
775
|
-
indexUpdatedAt: index.updatedAt,
|
|
819
|
+
...firstFields,
|
|
820
|
+
// Top-level fields describe the first account shown; ok covers all of them.
|
|
821
|
+
ok: checked.every((entry) => entry.ok),
|
|
776
822
|
claudeDesktopInstalled: claudeDesktop.installed,
|
|
777
|
-
|
|
823
|
+
...(isTruthyFlag(parsed.flags["check-updates"]) ? { update: await checkForUpdate(await getPkgVersion()) } : {}),
|
|
824
|
+
...(checked.length > 1 ? { accounts: checked } : {}),
|
|
778
825
|
};
|
|
779
826
|
process.stdout.write(wantJson ? json(result) : `${result.ok ? "Doctor OK" : "Doctor failed"}\n${json(result)}`);
|
|
780
827
|
if (!result.ok)
|
|
@@ -816,18 +863,71 @@ async function runConnectionStatus(parsed) {
|
|
|
816
863
|
process.exitCode = 1;
|
|
817
864
|
});
|
|
818
865
|
}
|
|
866
|
+
// a***@example.com: enough to tell accounts apart in a bug report, not enough to identify the person.
|
|
867
|
+
export function maskAddress(address) {
|
|
868
|
+
const at = address.lastIndexOf("@");
|
|
869
|
+
return at <= 0 ? "***" : `${address[0]}***${address.slice(at)}`;
|
|
870
|
+
}
|
|
871
|
+
async function runReport() {
|
|
872
|
+
await withServices(async ({ config, accountManager }) => {
|
|
873
|
+
const accounts = await Promise.all(accountManager.all().map(async (bundle) => {
|
|
874
|
+
let imap = "ok";
|
|
875
|
+
let folderCount;
|
|
876
|
+
try {
|
|
877
|
+
await bundle.imapService.ping();
|
|
878
|
+
folderCount = (await bundle.imapService.getFolders(true)).length;
|
|
879
|
+
}
|
|
880
|
+
catch (error) {
|
|
881
|
+
imap = diagnoseConnectionFailure(error)?.cause ?? "failed";
|
|
882
|
+
}
|
|
883
|
+
let smtp = "ok";
|
|
884
|
+
try {
|
|
885
|
+
await bundle.smtpService.verifyConnection();
|
|
886
|
+
}
|
|
887
|
+
catch (error) {
|
|
888
|
+
smtp = diagnoseConnectionFailure(error)?.cause ?? "failed";
|
|
889
|
+
}
|
|
890
|
+
const index = await bundle.localIndexService.getStatus();
|
|
891
|
+
return {
|
|
892
|
+
account: maskAddress(bundle.account.address),
|
|
893
|
+
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 },
|
|
895
|
+
folderCount,
|
|
896
|
+
index: { storedMessageCount: index.storedMessageCount, updatedAt: index.updatedAt, isStale: index.isStale },
|
|
897
|
+
backgroundSync: { folder: bundle.backgroundSyncService.getStatus().folder, full: bundle.backgroundSyncService.getStatus().full },
|
|
898
|
+
};
|
|
899
|
+
}));
|
|
900
|
+
const claudeDesktop = await getClaudeDesktopInstallStatus();
|
|
901
|
+
process.stdout.write(json({
|
|
902
|
+
version: await getPkgVersion(),
|
|
903
|
+
node: process.version,
|
|
904
|
+
platform: `${process.platform} ${process.arch}`,
|
|
905
|
+
claudeDesktopInstalled: claudeDesktop.installed,
|
|
906
|
+
runtime: sanitizeRuntimeConfig(config.runtime),
|
|
907
|
+
accounts,
|
|
908
|
+
}));
|
|
909
|
+
});
|
|
910
|
+
}
|
|
819
911
|
async function runRuntimeStatus(parsed) {
|
|
820
912
|
const wantJson = isTruthyFlag(parsed.flags.json);
|
|
821
|
-
await withServices(async ({
|
|
822
|
-
const
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
913
|
+
await withServices(async ({ accountManager }) => {
|
|
914
|
+
const bundles = selectAccountBundles(accountManager, parsed);
|
|
915
|
+
const perAccount = await Promise.all(bundles.map(async (bundle) => ({
|
|
916
|
+
slug: bundle.account.slug,
|
|
917
|
+
account: bundle.config.smtp.username,
|
|
918
|
+
runtime: sanitizeRuntimeConfig(bundle.config.runtime),
|
|
919
|
+
backgroundSync: bundle.backgroundSyncService.getStatus(),
|
|
920
|
+
health: healthProblems(bundle.backgroundSyncService.getStatus(), await bundle.localIndexService.getStatus()),
|
|
921
|
+
localIndex: await bundle.localIndexService.getStatus(),
|
|
827
922
|
drafts: {
|
|
828
|
-
total: (await draftStore.listDrafts(true)).length,
|
|
829
|
-
active: (await draftStore.listDrafts(false)).length,
|
|
923
|
+
total: (await bundle.draftStore.listDrafts(true)).length,
|
|
924
|
+
active: (await bundle.draftStore.listDrafts(false)).length,
|
|
830
925
|
},
|
|
926
|
+
})));
|
|
927
|
+
const { slug: _slug, ...firstFields } = perAccount[0];
|
|
928
|
+
const result = {
|
|
929
|
+
...firstFields,
|
|
930
|
+
...(perAccount.length > 1 ? { accounts: perAccount } : {}),
|
|
831
931
|
};
|
|
832
932
|
process.stdout.write(wantJson ? json(result) : `${JSON.stringify(result, null, 2)}\n`);
|
|
833
933
|
});
|
|
@@ -1973,9 +2073,19 @@ export async function main() {
|
|
|
1973
2073
|
process.stdout.write(scheduleText(parsed));
|
|
1974
2074
|
return;
|
|
1975
2075
|
case "-v":
|
|
1976
|
-
case "version":
|
|
1977
|
-
|
|
2076
|
+
case "version": {
|
|
2077
|
+
const current = await getPkgVersion();
|
|
2078
|
+
process.stdout.write(`proton-mail-bridge-client ${current}\n`);
|
|
2079
|
+
if (isTruthyFlag(parsed.flags.check)) {
|
|
2080
|
+
const update = await checkForUpdate(current);
|
|
2081
|
+
process.stdout.write(update.error
|
|
2082
|
+
? `Could not check for updates: ${update.error}\n`
|
|
2083
|
+
: update.newer
|
|
2084
|
+
? `A newer release is available: ${update.latest} (${update.releaseUrl}). Update with: npm install -g proton-mail-bridge-client\n`
|
|
2085
|
+
: "You are on the latest release.\n");
|
|
2086
|
+
}
|
|
1978
2087
|
return;
|
|
2088
|
+
}
|
|
1979
2089
|
case "setup-claude-desktop":
|
|
1980
2090
|
await runClaudeDesktopSetupWizard();
|
|
1981
2091
|
return;
|
|
@@ -1988,6 +2098,9 @@ export async function main() {
|
|
|
1988
2098
|
case "connection-status":
|
|
1989
2099
|
await runConnectionStatus(parsed);
|
|
1990
2100
|
return;
|
|
2101
|
+
case "report":
|
|
2102
|
+
await runReport();
|
|
2103
|
+
return;
|
|
1991
2104
|
case "runtime-status":
|
|
1992
2105
|
await runRuntimeStatus(parsed);
|
|
1993
2106
|
return;
|