proton-mail-bridge-client 2.16.3 → 2.17.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 +149 -12
- package/dist/cli.d.ts +16 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +178 -1
- package/dist/cli.js.map +1 -1
- package/dist/config-env.d.ts +2 -1
- package/dist/config-env.d.ts.map +1 -1
- package/dist/config-env.js +24 -0
- package/dist/config-env.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +172 -8
- package/dist/index.js.map +1 -1
- package/dist/services/account-manager.d.ts +7 -0
- package/dist/services/account-manager.d.ts.map +1 -1
- package/dist/services/account-manager.js +59 -0
- package/dist/services/account-manager.js.map +1 -1
- package/dist/services/audit-service.d.ts +20 -0
- package/dist/services/audit-service.d.ts.map +1 -1
- package/dist/services/audit-service.js +266 -9
- package/dist/services/audit-service.js.map +1 -1
- package/dist/services/autoresponder-service.d.ts +35 -6
- package/dist/services/autoresponder-service.d.ts.map +1 -1
- package/dist/services/autoresponder-service.js +223 -108
- package/dist/services/autoresponder-service.js.map +1 -1
- package/dist/services/brief-service.d.ts.map +1 -1
- package/dist/services/brief-service.js +3 -2
- package/dist/services/brief-service.js.map +1 -1
- package/dist/services/category-service.d.ts +91 -0
- package/dist/services/category-service.d.ts.map +1 -0
- package/dist/services/category-service.js +320 -0
- package/dist/services/category-service.js.map +1 -0
- package/dist/services/commitment-service.d.ts.map +1 -1
- package/dist/services/commitment-service.js +3 -2
- package/dist/services/commitment-service.js.map +1 -1
- package/dist/services/contact-service.d.ts +60 -0
- package/dist/services/contact-service.d.ts.map +1 -0
- package/dist/services/contact-service.js +248 -0
- package/dist/services/contact-service.js.map +1 -0
- package/dist/services/delivery-queue-service.d.ts +4 -0
- package/dist/services/delivery-queue-service.d.ts.map +1 -1
- package/dist/services/delivery-queue-service.js +12 -2
- package/dist/services/delivery-queue-service.js.map +1 -1
- package/dist/services/draft-store-service.d.ts.map +1 -1
- package/dist/services/draft-store-service.js +3 -2
- package/dist/services/draft-store-service.js.map +1 -1
- package/dist/services/embedding-service.d.ts +68 -0
- package/dist/services/embedding-service.d.ts.map +1 -0
- package/dist/services/embedding-service.js +465 -0
- package/dist/services/embedding-service.js.map +1 -0
- package/dist/services/event-log-service.d.ts +67 -0
- package/dist/services/event-log-service.d.ts.map +1 -0
- package/dist/services/event-log-service.js +280 -0
- package/dist/services/event-log-service.js.map +1 -0
- package/dist/services/grant-service.d.ts +84 -0
- package/dist/services/grant-service.d.ts.map +1 -0
- package/dist/services/grant-service.js +214 -0
- package/dist/services/grant-service.js.map +1 -0
- package/dist/services/local-index-service.d.ts +18 -1
- package/dist/services/local-index-service.d.ts.map +1 -1
- package/dist/services/local-index-service.js +54 -4
- package/dist/services/local-index-service.js.map +1 -1
- package/dist/services/reply-reminder-service.d.ts +4 -0
- package/dist/services/reply-reminder-service.d.ts.map +1 -1
- package/dist/services/reply-reminder-service.js +22 -2
- package/dist/services/reply-reminder-service.js.map +1 -1
- package/dist/services/rules-service.d.ts +9 -1
- package/dist/services/rules-service.d.ts.map +1 -1
- package/dist/services/rules-service.js +8 -3
- package/dist/services/rules-service.js.map +1 -1
- package/dist/services/sieve-export.d.ts +19 -0
- package/dist/services/sieve-export.d.ts.map +1 -0
- package/dist/services/sieve-export.js +192 -0
- package/dist/services/sieve-export.js.map +1 -0
- package/dist/services/simple-imap-service.d.ts +13 -0
- package/dist/services/simple-imap-service.d.ts.map +1 -1
- package/dist/services/simple-imap-service.js +140 -17
- package/dist/services/simple-imap-service.js.map +1 -1
- package/dist/services/snooze-service.d.ts +4 -0
- package/dist/services/snooze-service.d.ts.map +1 -1
- package/dist/services/snooze-service.js +12 -3
- package/dist/services/snooze-service.js.map +1 -1
- package/dist/services/template-service.d.ts.map +1 -1
- package/dist/services/template-service.js +3 -2
- package/dist/services/template-service.js.map +1 -1
- package/dist/services/triage-plan-service.d.ts +2 -1
- package/dist/services/triage-plan-service.d.ts.map +1 -1
- package/dist/services/triage-plan-service.js +3 -2
- package/dist/services/triage-plan-service.js.map +1 -1
- package/dist/tools/attachments.js +1 -1
- package/dist/tools/attachments.js.map +1 -1
- package/dist/tools/categories.d.ts +4 -0
- package/dist/tools/categories.d.ts.map +1 -0
- package/dist/tools/categories.js +269 -0
- package/dist/tools/categories.js.map +1 -0
- package/dist/tools/contacts.d.ts +6 -0
- package/dist/tools/contacts.d.ts.map +1 -0
- package/dist/tools/contacts.js +295 -0
- package/dist/tools/contacts.js.map +1 -0
- package/dist/tools/context.d.ts +4 -0
- package/dist/tools/context.d.ts.map +1 -1
- package/dist/tools/context.js +3 -0
- package/dist/tools/context.js.map +1 -1
- package/dist/tools/definitions.d.ts +333 -5
- package/dist/tools/definitions.d.ts.map +1 -1
- package/dist/tools/definitions.js +169 -5
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/delegation.d.ts +44 -0
- package/dist/tools/delegation.d.ts.map +1 -0
- package/dist/tools/delegation.js +180 -0
- package/dist/tools/delegation.js.map +1 -0
- package/dist/tools/diagnostics.d.ts.map +1 -1
- package/dist/tools/diagnostics.js +6 -0
- package/dist/tools/diagnostics.js.map +1 -1
- package/dist/tools/drafts-support.d.ts +2 -1
- package/dist/tools/drafts-support.d.ts.map +1 -1
- package/dist/tools/drafts-support.js +10 -2
- package/dist/tools/drafts-support.js.map +1 -1
- package/dist/tools/drafts.d.ts.map +1 -1
- package/dist/tools/drafts.js +8 -0
- package/dist/tools/drafts.js.map +1 -1
- package/dist/tools/events.d.ts +47 -0
- package/dist/tools/events.d.ts.map +1 -0
- package/dist/tools/events.js +240 -0
- package/dist/tools/events.js.map +1 -0
- package/dist/tools/reading.d.ts.map +1 -1
- package/dist/tools/reading.js +123 -3
- package/dist/tools/reading.js.map +1 -1
- package/dist/tools/resources.d.ts +4 -0
- package/dist/tools/resources.d.ts.map +1 -1
- package/dist/tools/resources.js +9 -0
- package/dist/tools/resources.js.map +1 -1
- package/dist/tools/rules-runner.d.ts.map +1 -1
- package/dist/tools/rules-runner.js +1 -0
- package/dist/tools/rules-runner.js.map +1 -1
- package/dist/tools/rules.d.ts +9 -0
- package/dist/tools/rules.d.ts.map +1 -1
- package/dist/tools/rules.js +50 -19
- package/dist/tools/rules.js.map +1 -1
- package/dist/tools/sending.d.ts.map +1 -1
- package/dist/tools/sending.js +4 -0
- package/dist/tools/sending.js.map +1 -1
- package/dist/tools/style.d.ts +2 -0
- package/dist/tools/style.d.ts.map +1 -1
- package/dist/tools/style.js +1 -1
- package/dist/tools/style.js.map +1 -1
- package/dist/tools/templates-and-reminders.d.ts +3 -0
- package/dist/tools/templates-and-reminders.d.ts.map +1 -1
- package/dist/tools/templates-and-reminders.js +31 -17
- package/dist/tools/templates-and-reminders.js.map +1 -1
- package/dist/tools/triage.d.ts.map +1 -1
- package/dist/tools/triage.js +6 -2
- package/dist/tools/triage.js.map +1 -1
- package/dist/types/index.d.ts +23 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/account-identity.d.ts.map +1 -1
- package/dist/utils/account-identity.js +3 -2
- package/dist/utils/account-identity.js.map +1 -1
- package/dist/utils/approval.d.ts +133 -0
- package/dist/utils/approval.d.ts.map +1 -0
- package/dist/utils/approval.js +507 -0
- package/dist/utils/approval.js.map +1 -0
- package/dist/utils/atomic-write.d.ts +1 -1
- package/dist/utils/atomic-write.d.ts.map +1 -1
- package/dist/utils/atomic-write.js +10 -3
- package/dist/utils/atomic-write.js.map +1 -1
- package/dist/utils/contact-text.d.ts +8 -0
- package/dist/utils/contact-text.d.ts.map +1 -0
- package/dist/utils/contact-text.js +24 -0
- package/dist/utils/contact-text.js.map +1 -0
- package/dist/utils/delegation.d.ts +17 -0
- package/dist/utils/delegation.d.ts.map +1 -0
- package/dist/utils/delegation.js +72 -0
- package/dist/utils/delegation.js.map +1 -0
- package/dist/utils/file-lock.d.ts.map +1 -1
- package/dist/utils/file-lock.js +5 -4
- package/dist/utils/file-lock.js.map +1 -1
- package/dist/utils/mail-security.d.ts +4 -2
- package/dist/utils/mail-security.d.ts.map +1 -1
- package/dist/utils/mail-security.js +357 -51
- package/dist/utils/mail-security.js.map +1 -1
- package/dist/utils/ocr.d.ts +24 -0
- package/dist/utils/ocr.d.ts.map +1 -0
- package/dist/utils/ocr.js +109 -0
- package/dist/utils/ocr.js.map +1 -0
- package/dist/utils/office-text-worker.d.ts +2 -0
- package/dist/utils/office-text-worker.d.ts.map +1 -0
- package/dist/utils/office-text-worker.js +17 -0
- package/dist/utils/office-text-worker.js.map +1 -0
- package/dist/utils/office-text.d.ts +32 -0
- package/dist/utils/office-text.d.ts.map +1 -0
- package/dist/utils/office-text.js +540 -0
- package/dist/utils/office-text.js.map +1 -0
- package/dist/utils/pdf-text-worker.js +2 -2
- package/dist/utils/pdf-text-worker.js.map +1 -1
- package/dist/utils/pdf-text.d.ts +10 -0
- package/dist/utils/pdf-text.d.ts.map +1 -1
- package/dist/utils/pdf-text.js +49 -9
- package/dist/utils/pdf-text.js.map +1 -1
- package/dist/utils/png.d.ts +2 -0
- package/dist/utils/png.d.ts.map +1 -0
- package/dist/utils/png.js +26 -0
- package/dist/utils/png.js.map +1 -0
- package/dist/utils/regular-file.d.ts +9 -0
- package/dist/utils/regular-file.d.ts.map +1 -0
- package/dist/utils/regular-file.js +34 -0
- package/dist/utils/regular-file.js.map +1 -0
- package/dist/utils/runtime-policy.d.ts.map +1 -1
- package/dist/utils/runtime-policy.js +2 -0
- package/dist/utils/runtime-policy.js.map +1 -1
- package/dist/utils/safe-write.d.ts +7 -1
- package/dist/utils/safe-write.d.ts.map +1 -1
- package/dist/utils/safe-write.js +27 -7
- package/dist/utils/safe-write.js.map +1 -1
- package/dist/utils/server-state-paths.d.ts +12 -0
- package/dist/utils/server-state-paths.d.ts.map +1 -0
- package/dist/utils/server-state-paths.js +69 -0
- package/dist/utils/server-state-paths.js.map +1 -0
- package/dist/utils/style-profile.d.ts +16 -0
- package/dist/utils/style-profile.d.ts.map +1 -1
- package/dist/utils/style-profile.js +37 -1
- package/dist/utils/style-profile.js.map +1 -1
- package/dist/utils/vcard.d.ts +27 -0
- package/dist/utils/vcard.d.ts.map +1 -0
- package/dist/utils/vcard.js +141 -0
- package/dist/utils/vcard.js.map +1 -0
- package/dist/utils/zip-reader.d.ts +16 -0
- package/dist/utils/zip-reader.d.ts.map +1 -0
- package/dist/utils/zip-reader.js +108 -0
- package/dist/utils/zip-reader.js.map +1 -0
- package/package.json +1 -1
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 { createInterface } from "node:readline";
|
|
5
6
|
import { homedir, release as osRelease } from "node:os";
|
|
6
7
|
import { promisify } from "node:util";
|
|
7
8
|
import { fileURLToPath } from "node:url";
|
|
@@ -10,8 +11,10 @@ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"
|
|
|
10
11
|
import { buildConfigFromEnv, createServer, withAudit } from "./index.js";
|
|
11
12
|
import { isMainModule } from "./is-main.js";
|
|
12
13
|
import { isLikelyAuthenticationError, isLikelyCertificateError, isLikelyConnectionError, isLikelyTlsMismatchError, SimpleIMAPService } from "./services/simple-imap-service.js";
|
|
14
|
+
import { GrantService, grantStatus } from "./services/grant-service.js";
|
|
15
|
+
import { GRANTABLE_TOOLS, MAX_GRANT_DAYS, MAX_GRANT_USES } from "./utils/delegation.js";
|
|
13
16
|
import { ensureDestructiveConfirmed, ensureEmailActionAllowed, ensureMailboxWriteAllowed, sanitizeRuntimeConfig } from "./utils/runtime-policy.js";
|
|
14
|
-
import { isValidEmail, parseEmails } from "./utils/helpers.js";
|
|
17
|
+
import { InvalidArgumentError, isValidEmail, parseEmails } from "./utils/helpers.js";
|
|
15
18
|
import { checkForUpdate } from "./utils/update-check.js";
|
|
16
19
|
import { healthProblems } from "./utils/health.js";
|
|
17
20
|
import { bridgeVersionNote } from "./utils/bridge-version.js";
|
|
@@ -176,6 +179,9 @@ function printHelp() {
|
|
|
176
179
|
" draft-send <id> Send a saved draft",
|
|
177
180
|
" draft-delete <id> Delete a saved draft",
|
|
178
181
|
" draft-thread-reply <id> Create a reply draft for a thread",
|
|
182
|
+
" grant-create Give the assistant a narrow, expiring permission to send (--tools --recipients --expires --uses --note); asks you to confirm at a terminal",
|
|
183
|
+
" grant-list List the delegation grants in force (--all, --account)",
|
|
184
|
+
" grant-revoke <id> Revoke a delegation grant",
|
|
179
185
|
" tools List every MCP tool exposed by the server",
|
|
180
186
|
" tool <name> Call any MCP tool with JSON arguments",
|
|
181
187
|
"",
|
|
@@ -300,6 +306,9 @@ export const COMMAND_SPECS = {
|
|
|
300
306
|
"draft-send": { usage: "draft-send <id> [--args '{...}']", description: "Send a saved draft (dryRun etc. via --args)", flags: ["id", "args", "args-file"] },
|
|
301
307
|
"draft-delete": { usage: "draft-delete <id>", description: "Delete a saved draft", flags: ["id"] },
|
|
302
308
|
"draft-thread-reply": { usage: "draft-thread-reply <threadId> (--body <text> | stdin) [--reply-all]", description: "Create a reply draft for a thread", flags: ["id", ...BODY_FLAGS, "reply-all", "all", ...DRAFT_COPY_FLAGS] },
|
|
309
|
+
"grant-create": { usage: "grant-create --tools <a,b> --recipients <addr|@domain,...> --expires <30m|12h|7d|ISO> [--uses <n>] [--note <text>] [--account <slug|address>]", description: `Give the assistant a narrow, expiring permission to send (tools: ${GRANTABLE_TOOLS.join(", ")}; every To/Cc/Bcc must match; at most ${MAX_GRANT_USES} uses and ${MAX_GRANT_DAYS} days). Honoured only by a server started with PROTONMAIL_DELEGATION=on`, flags: ["tools", "recipients", "expires", "uses", "note", "account"] },
|
|
310
|
+
"grant-list": { usage: "grant-list [--all] [--account <slug|address>]", description: "List the delegation grants in force (--all also shows used up, expired and revoked ones)", flags: ["all", "account"] },
|
|
311
|
+
"grant-revoke": { usage: "grant-revoke <id> [--account <slug|address>]", description: "Revoke a delegation grant at once", flags: ["account"] },
|
|
303
312
|
tools: { usage: "tools", description: "List every MCP tool exposed by the server", flags: [] },
|
|
304
313
|
tool: { usage: "tool <name> [--args '{...}' | --args-file <path>]", description: "Call any MCP tool with JSON arguments", flags: ["args", "args-file"] },
|
|
305
314
|
};
|
|
@@ -1967,6 +1976,154 @@ async function runDraftDelete(parsed) {
|
|
|
1967
1976
|
printToolCallResult(result, wantJson);
|
|
1968
1977
|
});
|
|
1969
1978
|
}
|
|
1979
|
+
// Delegation grants are made and revoked here and nowhere else: they are read and written straight from the account's data
|
|
1980
|
+
// directory, never through an MCP tool, so a model that has only MCP tools cannot grant itself anything.
|
|
1981
|
+
function grantStores(parsed, options) {
|
|
1982
|
+
const { accounts } = buildConfigFromEnv();
|
|
1983
|
+
const wanted = getStringFlag(parsed.flags, "account");
|
|
1984
|
+
const chosen = wanted ? accounts.filter((account) => account.address.toLowerCase() === wanted.toLowerCase() || account.slug === wanted) : accounts;
|
|
1985
|
+
if (chosen.length === 0)
|
|
1986
|
+
throw new CliUsageError(`Unknown account "${wanted}". Configured: ${accounts.map((account) => account.slug).join(", ")}`);
|
|
1987
|
+
if (options.single && !wanted && chosen.length > 1)
|
|
1988
|
+
throw new CliUsageError(`Several accounts are configured; name one with --account (${accounts.map((account) => account.slug).join(", ")}).`);
|
|
1989
|
+
return chosen.map((account) => ({ account, grants: new GrantService({ dataDir: account.dataDir, smtp: account.smtp }, account.address) }));
|
|
1990
|
+
}
|
|
1991
|
+
const GRANT_SPAN_MS = { m: 60_000, h: 3_600_000, d: 86_400_000 };
|
|
1992
|
+
// "30m", "12h", "7d" from now, or a timestamp (a bare date is midnight UTC).
|
|
1993
|
+
export function parseGrantExpiry(value, now = new Date()) {
|
|
1994
|
+
const span = /^(\d{1,4})([mhd])$/.exec(value.trim());
|
|
1995
|
+
const date = span
|
|
1996
|
+
? new Date(now.getTime() + Number(span[1]) * GRANT_SPAN_MS[span[2]])
|
|
1997
|
+
: /^\d{4}-\d{2}-\d{2}/.test(value.trim())
|
|
1998
|
+
? new Date(value.trim())
|
|
1999
|
+
: new Date(Number.NaN);
|
|
2000
|
+
if (Number.isNaN(date.getTime()))
|
|
2001
|
+
throw new CliUsageError(`--expires must be like 30m, 12h, 7d or a timestamp such as 2026-11-05T18:00:00Z, got "${value}".`);
|
|
2002
|
+
return date;
|
|
2003
|
+
}
|
|
2004
|
+
const grantLine = (grant, now) => [
|
|
2005
|
+
grant.id,
|
|
2006
|
+
grant.account,
|
|
2007
|
+
grant.tools.join(","),
|
|
2008
|
+
grant.recipients.join(","),
|
|
2009
|
+
`${grant.maxUses - grant.usedCount}/${grant.maxUses} uses left`,
|
|
2010
|
+
`expires ${grant.expiresAt}`,
|
|
2011
|
+
...(grantStatus(grant, now) === "active" ? [] : [grantStatus(grant, now)]),
|
|
2012
|
+
JSON.stringify(grant.note),
|
|
2013
|
+
].join(" | ");
|
|
2014
|
+
// A grant is made or revoked only by a person at a terminal: both stdin and stdout must be one, the whole grant is shown, and
|
|
2015
|
+
// a phrase that names its recipient count (or id) is typed back. There is no flag, variable or argument that answers for the
|
|
2016
|
+
// person, so a model that can run shell commands is stopped here (one that can write grants.json itself is not: the file is not signed).
|
|
2017
|
+
export const isInteractiveTerminal = (input = process.stdin, output = process.stdout) => Boolean(input?.isTTY && output?.isTTY);
|
|
2018
|
+
export const grantCreatePhrase = (recipientCount) => `grant ${recipientCount} recipient${recipientCount === 1 ? "" : "s"}`;
|
|
2019
|
+
export const grantRevokePhrase = (id) => `revoke ${id}`;
|
|
2020
|
+
const grantTerminal = {
|
|
2021
|
+
isInteractive: () => isInteractiveTerminal(),
|
|
2022
|
+
ask: (text) => {
|
|
2023
|
+
const lines = createInterface({ input: process.stdin, output: process.stdout });
|
|
2024
|
+
return new Promise((resolve) => {
|
|
2025
|
+
lines.once("close", () => resolve(""));
|
|
2026
|
+
lines.question(text, resolve);
|
|
2027
|
+
}).finally(() => lines.close());
|
|
2028
|
+
},
|
|
2029
|
+
write: (text) => void process.stdout.write(text),
|
|
2030
|
+
};
|
|
2031
|
+
function requireTerminal(terminal, what) {
|
|
2032
|
+
if (!terminal.isInteractive()) {
|
|
2033
|
+
throw new Error(`${what} needs an interactive terminal (stdin and stdout) and a person to confirm it; it cannot be run from a script or by the assistant. Nothing was changed.`);
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
async function confirmAtTerminal(terminal, summary, phrase) {
|
|
2037
|
+
const answer = await terminal.ask(`${summary}\nTo confirm, type exactly: ${phrase}\n> `);
|
|
2038
|
+
if (answer.trim() !== phrase)
|
|
2039
|
+
throw new Error("Not confirmed. Nothing was changed.");
|
|
2040
|
+
}
|
|
2041
|
+
export async function runGrantCreate(parsed, terminal = grantTerminal) {
|
|
2042
|
+
const [{ account, grants }] = grantStores(parsed, { single: true });
|
|
2043
|
+
const split = (key) => (getStringFlag(parsed.flags, key) ?? "").split(/[,\s]+/).filter(Boolean);
|
|
2044
|
+
const tools = split("tools");
|
|
2045
|
+
const recipients = split("recipients");
|
|
2046
|
+
const expires = getStringFlag(parsed.flags, "expires");
|
|
2047
|
+
if (tools.length === 0)
|
|
2048
|
+
throw new CliUsageError(`--tools is required: ${GRANTABLE_TOOLS.join(", ")} (comma-separated).`);
|
|
2049
|
+
if (recipients.length === 0)
|
|
2050
|
+
throw new CliUsageError("--recipients is required: exact addresses and/or @domain entries (comma-separated).");
|
|
2051
|
+
if (!expires)
|
|
2052
|
+
throw new CliUsageError(`--expires is required (30m, 12h, 7d or a timestamp; at most ${MAX_GRANT_DAYS} days).`);
|
|
2053
|
+
const maxUses = parseIntegerFlag(parsed.flags, "uses", 1, `a whole number from 1 to ${MAX_GRANT_USES}`) ?? 1;
|
|
2054
|
+
const input = { note: getTextFlag(parsed.flags, "note"), tools, recipients, maxUses, expiresAt: parseGrantExpiry(expires) };
|
|
2055
|
+
let plan;
|
|
2056
|
+
try {
|
|
2057
|
+
plan = grants.planGrant(input);
|
|
2058
|
+
}
|
|
2059
|
+
catch (error) {
|
|
2060
|
+
if (error instanceof InvalidArgumentError)
|
|
2061
|
+
throw new CliUsageError(error.message);
|
|
2062
|
+
throw error;
|
|
2063
|
+
}
|
|
2064
|
+
requireTerminal(terminal, "grant-create");
|
|
2065
|
+
await confirmAtTerminal(terminal, [
|
|
2066
|
+
`New grant for ${account.address}:`,
|
|
2067
|
+
` tools: ${plan.tools.join(", ")}`,
|
|
2068
|
+
` recipients: ${plan.recipients.join(", ")} (every To, Cc and Bcc must match)`,
|
|
2069
|
+
` uses: ${plan.maxUses}`,
|
|
2070
|
+
` expires: ${plan.expiresAt.toISOString()}`,
|
|
2071
|
+
` note: ${JSON.stringify(plan.note)}`,
|
|
2072
|
+
"With PROTONMAIL_DELEGATION=on the assistant can then send these without asking you again.",
|
|
2073
|
+
].join("\n"), grantCreatePhrase(plan.recipients.length));
|
|
2074
|
+
let grant;
|
|
2075
|
+
try {
|
|
2076
|
+
grant = await grants.createGrant({ ...input, expiresAt: plan.expiresAt });
|
|
2077
|
+
}
|
|
2078
|
+
catch (error) {
|
|
2079
|
+
if (error instanceof InvalidArgumentError)
|
|
2080
|
+
throw new CliUsageError(error.message);
|
|
2081
|
+
throw error;
|
|
2082
|
+
}
|
|
2083
|
+
if (isTruthyFlag(parsed.flags.json)) {
|
|
2084
|
+
terminal.write(json(grant));
|
|
2085
|
+
return;
|
|
2086
|
+
}
|
|
2087
|
+
terminal.write([
|
|
2088
|
+
`Grant ${grant.id} created for ${account.address}.`,
|
|
2089
|
+
` tools: ${grant.tools.join(", ")}`,
|
|
2090
|
+
` recipients: ${grant.recipients.join(", ")} (every To, Cc and Bcc must match)`,
|
|
2091
|
+
` uses: ${grant.maxUses}`,
|
|
2092
|
+
` expires: ${grant.expiresAt}`,
|
|
2093
|
+
` note: ${JSON.stringify(grant.note)}`,
|
|
2094
|
+
"Only a server started with PROTONMAIL_DELEGATION=on honours it. Revoke it with: grant-revoke " + grant.id,
|
|
2095
|
+
"",
|
|
2096
|
+
].join("\n"));
|
|
2097
|
+
}
|
|
2098
|
+
async function runGrantList(parsed) {
|
|
2099
|
+
const now = new Date();
|
|
2100
|
+
const all = isTruthyFlag(parsed.flags.all);
|
|
2101
|
+
const found = (await Promise.all(grantStores(parsed, { single: false }).map(({ grants }) => grants.list()))).flat();
|
|
2102
|
+
const shown = found
|
|
2103
|
+
.filter((grant) => all || grantStatus(grant, now) === "active")
|
|
2104
|
+
.sort((left, right) => Date.parse(left.expiresAt) - Date.parse(right.expiresAt));
|
|
2105
|
+
if (isTruthyFlag(parsed.flags.json)) {
|
|
2106
|
+
process.stdout.write(json(shown.map((grant) => ({ ...grant, status: grantStatus(grant, now) }))));
|
|
2107
|
+
return;
|
|
2108
|
+
}
|
|
2109
|
+
process.stdout.write(shown.length === 0 ? `No ${all ? "" : "active "}grants.\n` : `${shown.map((grant) => grantLine(grant, now)).join("\n")}\n`);
|
|
2110
|
+
}
|
|
2111
|
+
export async function runGrantRevoke(parsed, terminal = grantTerminal) {
|
|
2112
|
+
const id = parsed.positionals[0];
|
|
2113
|
+
if (!id)
|
|
2114
|
+
throw new CliUsageError("grant-revoke requires a grant id (see grant-list).");
|
|
2115
|
+
requireTerminal(terminal, "grant-revoke");
|
|
2116
|
+
for (const { account, grants } of grantStores(parsed, { single: false })) {
|
|
2117
|
+
const grant = (await grants.list()).find((candidate) => candidate.id === id);
|
|
2118
|
+
if (!grant)
|
|
2119
|
+
continue;
|
|
2120
|
+
await confirmAtTerminal(terminal, `Revoke this grant for ${account.address}:\n ${grantLine(grant, new Date())}`, grantRevokePhrase(grant.id));
|
|
2121
|
+
const result = await grants.revokeGrant(id);
|
|
2122
|
+
terminal.write(isTruthyFlag(parsed.flags.json) ? json({ id, account: account.address, revoked: true, alreadyRevoked: result.alreadyRevoked }) : `${result.alreadyRevoked ? "Already revoked" : "Revoked"}: ${id} (${account.address})\n`);
|
|
2123
|
+
return;
|
|
2124
|
+
}
|
|
2125
|
+
throw new Error(`No grant ${JSON.stringify(id.slice(0, 40))} found.`);
|
|
2126
|
+
}
|
|
1970
2127
|
async function runTools(parsed) {
|
|
1971
2128
|
const wantJson = isTruthyFlag(parsed.flags.json);
|
|
1972
2129
|
await withMcpClient(async (client) => {
|
|
@@ -2031,17 +2188,27 @@ export const TOOL_ONLY_COMMANDS = [
|
|
|
2031
2188
|
{ command: "rules", tool: "get_rules", positionals: [], help: "Show the mailbox rules, the proposals waiting and what the rules did (--account)" },
|
|
2032
2189
|
{ command: "set-rule", tool: "set_rule", positionals: [], help: "Create, change or accept a proposal as a rule (--proposalId, --id, --name, --enabled, --when, --actions, --confirmed)" },
|
|
2033
2190
|
{ command: "delete-rule", tool: "delete_rule", positionals: ["id"], help: "Delete a rule, or dismiss a proposal" },
|
|
2191
|
+
{ command: "export-sieve", tool: "export_sieve", positionals: [], help: "Turn the rules into a Sieve script for Proton's own filters, listing what cannot be expressed (--ruleId, --account)" },
|
|
2034
2192
|
{ command: "apply-rules", tool: "apply_rules", positionals: [], help: "Run the rules on mail already in the index; a preview unless --dryRun false (--ruleId, --limit, --confirmed)" },
|
|
2035
2193
|
{ command: "plan-inbox", tool: "plan_inbox", positionals: [], help: "Prepare a batch of mailbox changes to approve at once (--args '{\"scan\":true,\"limit\":30}')" },
|
|
2036
2194
|
{ command: "approve-plan", tool: "approve_plan", positionals: ["planId"], help: "Apply a plan from plan-inbox (--args '{\"exclude\":[3]}')" },
|
|
2037
2195
|
{ command: "undo-plan", tool: "undo_plan", positionals: ["planId"], help: "Put back what an approved plan changed" },
|
|
2196
|
+
{ command: "define-category", tool: "define_category", positionals: ["name"], help: "Define or change an inbox category of your own (--args '{\"keywords\":[\"invoice\"],\"label\":\"Receipts\"}'; '{\"starter\":true}' for the starter set)" },
|
|
2197
|
+
{ command: "categories", tool: "list_categories", positionals: [], help: "Your inbox categories and what corrections taught them (--account)" },
|
|
2198
|
+
{ command: "categorize-inbox", tool: "categorize_inbox", positionals: [], help: "Sort recent INBOX mail into your categories with the reasons; a preview unless --args '{\"dryRun\":false}' (then a plan for approve-plan)" },
|
|
2199
|
+
{ command: "correct-category", tool: "correct_category", positionals: ["emailId", "category"], help: "Teach the categories where a message belongs; shows what else would change" },
|
|
2038
2200
|
{ command: "find-commitments", tool: "find_commitments", positionals: [], help: "Find sentences that look like promises in recent mail (--args '{\"days\":7,\"direction\":\"both\"}')" },
|
|
2039
2201
|
{ command: "set-commitment", tool: "set_commitment", positionals: [], help: "Record a commitment, or close one (--args '{\"text\":\"...\",\"direction\":\"mine\",\"due\":\"2026-11-05\"}')" },
|
|
2040
2202
|
{ command: "commitments", tool: "list_commitments", positionals: [], help: "The commitment ledger: overdue, due today, open, probably done (--args '{\"status\":\"all\"}')" },
|
|
2203
|
+
{ command: "find-contact", tool: "find_contact", positionals: ["query"], help: "A person by name or address: addresses, organisation, tags, notes, counts both ways, last subjects" },
|
|
2204
|
+
{ command: "set-contact", tool: "set_contact", positionals: ["address"], help: "Tag, name, annotate, merge or unmerge a contact (--args '{\"addTags\":[\"vip\"]}' or '{\"mergeWith\":\"other@example.com\"}' or '{\"unmerge\":true}')" },
|
|
2205
|
+
{ command: "import-contacts", tool: "import_contacts", positionals: ["path"], help: "Import people from a local .vcf file into the contact store" },
|
|
2041
2206
|
{ command: "style-profile", tool: "get_style_profile", positionals: [], help: "How you write, from your sent mail: language, greeting, closing, tone (--args '{\"recipient\":\"pat@client.example\",\"refresh\":true}')" },
|
|
2042
2207
|
{ command: "get-brief", tool: "get_brief", positionals: [], help: "(tool form; see also `brief`)" },
|
|
2043
2208
|
{ command: "autoresponder", tool: "get_autoresponder", positionals: [], help: "Show the automatic reply of an address (--account)" },
|
|
2044
2209
|
{ 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)" },
|
|
2210
|
+
{ command: "list-grants", tool: "list_grants", positionals: [], help: "(tool form; see also `grant-list`)" },
|
|
2211
|
+
{ command: "events", tool: "get_events", positionals: [], help: "What happened while nobody asked: new mail, snoozes, reminders, sends, rules, plans (--args '{\"sinceCursor\":\"<nextCursor>\",\"types\":[\"mail\"],\"limit\":50}')" },
|
|
2045
2212
|
{ command: "list-templates", tool: "list_templates", positionals: [], help: "List saved email templates" },
|
|
2046
2213
|
{ command: "get-template", tool: "get_template", positionals: ["id"], help: "Get a saved template by id" },
|
|
2047
2214
|
{ command: "delete-template", tool: "delete_template", positionals: ["id"], help: "Delete a saved template" },
|
|
@@ -2080,6 +2247,7 @@ export const TOOL_ONLY_COMMANDS = [
|
|
|
2080
2247
|
{ command: "import-email", tool: "import_email", positionals: [], fileFieldBase64: "rawBase64", help: "Import a .eml message via IMAP APPEND (--file <path.eml>)" },
|
|
2081
2248
|
{ command: "clear-index", tool: "clear_index", positionals: [], help: "Delete the local SQLite mailbox index" },
|
|
2082
2249
|
{ command: "get-audit-logs", tool: "get_audit_logs", positionals: [], help: "Recent write-operation audit log entries" },
|
|
2250
|
+
{ command: "verify-audit-log", tool: "verify_audit_log", positionals: [], help: "Check the audit log hash chain and print its head hash" },
|
|
2083
2251
|
];
|
|
2084
2252
|
async function runToolOnlyCommand(entry, parsed) {
|
|
2085
2253
|
const wantJson = isTruthyFlag(parsed.flags.json);
|
|
@@ -2376,6 +2544,15 @@ export async function main() {
|
|
|
2376
2544
|
case "draft-thread-reply":
|
|
2377
2545
|
await runDraftThreadReply(parsed);
|
|
2378
2546
|
return;
|
|
2547
|
+
case "grant-create":
|
|
2548
|
+
await runGrantCreate(parsed);
|
|
2549
|
+
return;
|
|
2550
|
+
case "grant-list":
|
|
2551
|
+
await runGrantList(parsed);
|
|
2552
|
+
return;
|
|
2553
|
+
case "grant-revoke":
|
|
2554
|
+
await runGrantRevoke(parsed);
|
|
2555
|
+
return;
|
|
2379
2556
|
case "tools":
|
|
2380
2557
|
await runTools(parsed);
|
|
2381
2558
|
return;
|