proton-mail-bridge-client 2.11.0 → 2.12.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 +1 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +89 -39
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +36 -13
- 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 +42 -0
- package/dist/services/local-index-service.js.map +1 -1
- package/dist/services/simple-imap-service.d.ts +3 -0
- package/dist/services/simple-imap-service.d.ts.map +1 -1
- package/dist/services/simple-imap-service.js +58 -3
- 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 +6 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/helpers.d.ts.map +1 -1
- package/dist/utils/helpers.js +14 -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/package.json +1 -1
package/README.md
CHANGED
|
@@ -695,6 +695,7 @@ To check a real Bridge yourself, run the read-only smoke test: `PROTONMAIL_USERN
|
|
|
695
695
|
- `get_emails` and `search_emails` return a composite `emailId` — use it for all subsequent reads and actions.
|
|
696
696
|
- `search_indexed_emails` supports `from:`, `to:`, `subject:`, `label:`, `domain:` shortcuts.
|
|
697
697
|
- The local index lives at `PROTONMAIL_DATA_DIR/mail-index.sqlite`. Background sync and IMAP IDLE keep it warm.
|
|
698
|
+
- **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
699
|
- 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
700
|
- Audit logs live at `PROTONMAIL_DATA_DIR/audit.log`.
|
|
700
701
|
- 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,10 @@ 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;
|
|
27
31
|
interface ToolOnlyCommand {
|
|
28
32
|
command: string;
|
|
29
33
|
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":";AA8BA,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,CAoErD,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;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AA8BA,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,CAoErD,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;AAupCD,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,CAqO1C;AA4ED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAGzD"}
|
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ 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
14
|
import { buildSchedule, defaultSchedulerSystem, findStableAlias, parseTimeOfDay, SCHEDULER_SYSTEMS } from "./utils/schedule-templates.js";
|
|
@@ -235,9 +235,9 @@ export const COMMAND_SPECS = {
|
|
|
235
235
|
"setup-claude-desktop": { usage: "setup-claude-desktop", description: "Run the Claude Desktop setup wizard (works from any install)", flags: [] },
|
|
236
236
|
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
237
|
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: [] },
|
|
238
|
+
doctor: { usage: "doctor [--account <slug|address>]", description: "Verify IMAP, SMTP, and Claude Desktop wiring for every account, or one (exits 1 when the check fails)", flags: ["account"] },
|
|
239
239
|
"connection-status": { usage: "connection-status", description: "Show live IMAP/SMTP connectivity state (exits 1 when either is unreachable)", flags: [] },
|
|
240
|
-
"runtime-status": { usage: "runtime-status", description: "Show runtime policy and background sync state", flags: [] },
|
|
240
|
+
"runtime-status": { usage: "runtime-status [--account <slug|address>]", description: "Show runtime policy and background sync state, per account", flags: ["account"] },
|
|
241
241
|
sync: { usage: "sync [--folder <name>] [--limit <n>] [--full]", description: "Refresh the local index from Proton Bridge", flags: ["folder", "limit", "full", "no-attachment-text"] },
|
|
242
242
|
"index-status": { usage: "index-status", description: "Show local index health and freshness", flags: [] },
|
|
243
243
|
folders: { usage: "folders", description: "List available folders from Proton Bridge", flags: [] },
|
|
@@ -655,9 +655,22 @@ async function withServices(run) {
|
|
|
655
655
|
return await run({ config, ...services });
|
|
656
656
|
}
|
|
657
657
|
finally {
|
|
658
|
-
services.
|
|
659
|
-
|
|
658
|
+
for (const bundle of services.accountManager.all()) {
|
|
659
|
+
bundle.backgroundSyncService.stop();
|
|
660
|
+
await Promise.allSettled([bundle.imapService.disconnect(), bundle.smtpService.close()]);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
// --account picks one account (slug or address); without it, every configured account.
|
|
665
|
+
function selectAccountBundles(accountManager, parsed) {
|
|
666
|
+
const wanted = getStringFlag(parsed.flags, "account");
|
|
667
|
+
if (!wanted)
|
|
668
|
+
return accountManager.all();
|
|
669
|
+
const bundle = accountManager.byAddress(wanted) ?? accountManager.all().find((entry) => entry.account.slug === wanted);
|
|
670
|
+
if (!bundle) {
|
|
671
|
+
throw new CliUsageError(`Unknown account "${wanted}". Configured: ${accountManager.all().map((entry) => entry.account.slug).join(", ")}`);
|
|
660
672
|
}
|
|
673
|
+
return [bundle];
|
|
661
674
|
}
|
|
662
675
|
async function syncIndex(context, input) {
|
|
663
676
|
const snapshot = await context.imapService.collectEmailsForIndex({
|
|
@@ -740,41 +753,71 @@ async function runStatus(parsed) {
|
|
|
740
753
|
process.stdout.write(wantJson ? json(result) : renderStatus(result));
|
|
741
754
|
});
|
|
742
755
|
}
|
|
756
|
+
// Same classification as the run_doctor tool, so the CLI explains a failure instead of printing the raw error.
|
|
757
|
+
export function diagnoseConnectionFailure(reason) {
|
|
758
|
+
if (isLikelyAuthenticationError(reason)) {
|
|
759
|
+
return {
|
|
760
|
+
cause: "authentication_failed",
|
|
761
|
+
suggestion: "PROTONMAIL_PASSWORD must be the Bridge password (Bridge app -> account -> Mailbox details), not your Proton account password.",
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
if (isLikelyTlsMismatchError(reason)) {
|
|
765
|
+
return {
|
|
766
|
+
cause: "tls_mismatch",
|
|
767
|
+
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).",
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
if (isLikelyConnectionError(reason)) {
|
|
771
|
+
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." };
|
|
772
|
+
}
|
|
773
|
+
return undefined;
|
|
774
|
+
}
|
|
743
775
|
async function runDoctor(parsed) {
|
|
744
776
|
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
|
-
|
|
777
|
+
await withServices(async ({ accountManager }) => {
|
|
778
|
+
const bundles = selectAccountBundles(accountManager, parsed);
|
|
779
|
+
const checked = await Promise.all(bundles.map(async (bundle) => {
|
|
780
|
+
let imapOk = false;
|
|
781
|
+
let smtpOk = false;
|
|
782
|
+
let folderCount = 0;
|
|
783
|
+
let error;
|
|
784
|
+
let diagnosis;
|
|
785
|
+
try {
|
|
786
|
+
await bundle.imapService.ping();
|
|
787
|
+
imapOk = true;
|
|
788
|
+
folderCount = (await bundle.imapService.getFolders(true)).length;
|
|
789
|
+
await bundle.smtpService.verifyConnection();
|
|
790
|
+
smtpOk = true;
|
|
791
|
+
}
|
|
792
|
+
catch (err) {
|
|
793
|
+
error = err instanceof Error ? err.message : String(err);
|
|
794
|
+
diagnosis = diagnoseConnectionFailure(err);
|
|
795
|
+
}
|
|
796
|
+
return {
|
|
797
|
+
slug: bundle.account.slug,
|
|
798
|
+
account: bundle.config.smtp.username,
|
|
799
|
+
ok: imapOk && smtpOk,
|
|
800
|
+
imapOk,
|
|
801
|
+
smtpOk,
|
|
802
|
+
folderCount,
|
|
803
|
+
indexUpdatedAt: (await bundle.localIndexService.getStatus()).updatedAt,
|
|
804
|
+
error,
|
|
805
|
+
...(diagnosis ? { cause: diagnosis.cause, suggestion: diagnosis.suggestion } : {}),
|
|
806
|
+
};
|
|
807
|
+
}));
|
|
808
|
+
const claudeDesktop = await getClaudeDesktopInstallStatus();
|
|
809
|
+
const { slug: _slug, ...firstFields } = checked[0];
|
|
764
810
|
const result = {
|
|
765
|
-
ok: imapOk && smtpOk,
|
|
766
811
|
// A stale/orphaned install elsewhere on disk (wrong path, old
|
|
767
812
|
// version) looks identical to a fresh one on every other field
|
|
768
813
|
// above — this is the field that actually distinguishes them.
|
|
769
814
|
version: await getPkgVersion(),
|
|
770
815
|
entrypoint: cliEntryPath(),
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
folderCount,
|
|
775
|
-
indexUpdatedAt: index.updatedAt,
|
|
816
|
+
...firstFields,
|
|
817
|
+
// Top-level fields describe the first account shown; ok covers all of them.
|
|
818
|
+
ok: checked.every((entry) => entry.ok),
|
|
776
819
|
claudeDesktopInstalled: claudeDesktop.installed,
|
|
777
|
-
|
|
820
|
+
...(checked.length > 1 ? { accounts: checked } : {}),
|
|
778
821
|
};
|
|
779
822
|
process.stdout.write(wantJson ? json(result) : `${result.ok ? "Doctor OK" : "Doctor failed"}\n${json(result)}`);
|
|
780
823
|
if (!result.ok)
|
|
@@ -818,16 +861,23 @@ async function runConnectionStatus(parsed) {
|
|
|
818
861
|
}
|
|
819
862
|
async function runRuntimeStatus(parsed) {
|
|
820
863
|
const wantJson = isTruthyFlag(parsed.flags.json);
|
|
821
|
-
await withServices(async ({
|
|
822
|
-
const
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
864
|
+
await withServices(async ({ accountManager }) => {
|
|
865
|
+
const bundles = selectAccountBundles(accountManager, parsed);
|
|
866
|
+
const perAccount = await Promise.all(bundles.map(async (bundle) => ({
|
|
867
|
+
slug: bundle.account.slug,
|
|
868
|
+
account: bundle.config.smtp.username,
|
|
869
|
+
runtime: sanitizeRuntimeConfig(bundle.config.runtime),
|
|
870
|
+
backgroundSync: bundle.backgroundSyncService.getStatus(),
|
|
871
|
+
localIndex: await bundle.localIndexService.getStatus(),
|
|
827
872
|
drafts: {
|
|
828
|
-
total: (await draftStore.listDrafts(true)).length,
|
|
829
|
-
active: (await draftStore.listDrafts(false)).length,
|
|
873
|
+
total: (await bundle.draftStore.listDrafts(true)).length,
|
|
874
|
+
active: (await bundle.draftStore.listDrafts(false)).length,
|
|
830
875
|
},
|
|
876
|
+
})));
|
|
877
|
+
const { slug: _slug, ...firstFields } = perAccount[0];
|
|
878
|
+
const result = {
|
|
879
|
+
...firstFields,
|
|
880
|
+
...(perAccount.length > 1 ? { accounts: perAccount } : {}),
|
|
831
881
|
};
|
|
832
882
|
process.stdout.write(wantJson ? json(result) : `${JSON.stringify(result, null, 2)}\n`);
|
|
833
883
|
});
|