@rndmjoker/proton-mcp 0.4.4 → 0.4.7
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 +36 -12
- package/dist/mail/compose.d.ts +50 -24
- package/dist/mail/compose.js +62 -31
- package/dist/mail/compose.js.map +1 -1
- package/dist/mail/ids.d.ts +5 -1
- package/dist/mail/ids.js.map +1 -1
- package/dist/mail/markup.d.ts +66 -4
- package/dist/mail/markup.js +239 -41
- package/dist/mail/markup.js.map +1 -1
- package/dist/mail/messages.d.ts +5 -1
- package/dist/mail/messages.js.map +1 -1
- package/dist/mail/search.d.ts +8 -2
- package/dist/mail/search.js.map +1 -1
- package/dist/server.js +6 -0
- package/dist/server.js.map +1 -1
- package/dist/tools/confirm.d.ts +38 -5
- package/dist/tools/confirm.js +95 -49
- package/dist/tools/confirm.js.map +1 -1
- package/dist/tools/drafts.js +43 -9
- package/dist/tools/drafts.js.map +1 -1
- package/dist/tools/failures.d.ts +12 -0
- package/dist/tools/failures.js +24 -0
- package/dist/tools/failures.js.map +1 -1
- package/dist/tools/format.d.ts +4 -0
- package/dist/tools/format.js +76 -12
- package/dist/tools/format.js.map +1 -1
- package/dist/tools/guidance.js +61 -14
- package/dist/tools/guidance.js.map +1 -1
- package/dist/tools/interface.js +7 -3
- package/dist/tools/interface.js.map +1 -1
- package/dist/tools/pending-view.d.ts +46 -0
- package/dist/tools/pending-view.js +63 -0
- package/dist/tools/pending-view.js.map +1 -0
- package/dist/tools/preview.d.ts +70 -0
- package/dist/tools/preview.js +108 -0
- package/dist/tools/preview.js.map +1 -0
- package/dist/tools/send.js +53 -11
- package/dist/tools/send.js.map +1 -1
- package/dist/tools/updates.d.ts +53 -0
- package/dist/tools/updates.js +134 -0
- package/dist/tools/updates.js.map +1 -0
- package/dist/web/layout.js +34 -0
- package/dist/web/layout.js.map +1 -1
- package/dist/web/pending.d.ts +72 -0
- package/dist/web/pending.js +196 -0
- package/dist/web/pending.js.map +1 -0
- package/dist/web/sections.js +6 -1
- package/dist/web/sections.js.map +1 -1
- package/dist/web/security.js +10 -0
- package/dist/web/security.js.map +1 -1
- package/dist/web/server.d.ts +14 -1
- package/dist/web/server.js +39 -0
- package/dist/web/server.js.map +1 -1
- package/package.json +4 -1
package/dist/tools/format.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/tools/format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/tools/format.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/guidance.js
CHANGED
|
@@ -44,6 +44,10 @@ export const INSTRUCTIONS = [
|
|
|
44
44
|
' as though it were the result.',
|
|
45
45
|
'4. Nothing here deletes for good. "Delete" means moving to the trash.',
|
|
46
46
|
'',
|
|
47
|
+
'Everything about this server is seen and changed in a local page, and open_configuration',
|
|
48
|
+
'returns its address. Hand that address over whenever it is asked for, in whatever words: it',
|
|
49
|
+
'carries an access token, changes on every restart, and cannot be guessed or remembered.',
|
|
50
|
+
'',
|
|
47
51
|
'Two guides can be read when they are needed:',
|
|
48
52
|
' proton-mcp://guide/writing composing messages, formatting, quoting, what the',
|
|
49
53
|
' confirmation shows',
|
|
@@ -66,37 +70,72 @@ characters and the recipient reads the tags.
|
|
|
66
70
|
|
|
67
71
|
Headings, paragraphs, line breaks, rules, quotes, emphasis, lists, tables, links
|
|
68
72
|
and images. Styling by colour, background, font, size, weight, alignment,
|
|
69
|
-
spacing and borders.
|
|
70
|
-
|
|
73
|
+
spacing and borders.
|
|
74
|
+
|
|
75
|
+
**Images work, and the usual way is a plain web address.** An \`<img>\` pointing
|
|
76
|
+
at \`https://…\` is permitted with no restrictions on where it comes from, at any
|
|
77
|
+
size, inside a link, with rounded corners. **An animated GIF is permitted too**,
|
|
78
|
+
and it is one of the few things that really does animate in a mail client, since
|
|
79
|
+
CSS animation does not survive most of them.
|
|
80
|
+
|
|
81
|
+
The one refusal is a \`data:\` address, which would carry an entire file inside
|
|
82
|
+
the markup where nothing lists it as an attachment.
|
|
83
|
+
|
|
84
|
+
What is *not* possible is attaching a file from this machine; see Attachments
|
|
85
|
+
below. That is a separate thing from showing an image, and confusing the two
|
|
86
|
+
leads to writing a plainer message than necessary.
|
|
71
87
|
|
|
72
88
|
Anything else is **refused, not quietly removed**, and the answer names what and
|
|
73
89
|
why. A message that was silently altered is no longer the message anyone agreed
|
|
74
90
|
to, so there is nothing to guess at: if a send is refused, the reason says which
|
|
75
91
|
element or property and what to use instead.
|
|
76
92
|
|
|
77
|
-
|
|
93
|
+
Three refusals are worth understanding rather than working around:
|
|
78
94
|
|
|
79
95
|
- **Properties that hide content** (\`display\`, \`visibility\`, \`opacity\`,
|
|
80
96
|
\`position\`, a size of zero) are refused. They let a message be delivered
|
|
81
97
|
looking different from how it was written.
|
|
82
|
-
- **Addresses** are limited to \`http\`, \`https\`, \`mailto\` and \`cid
|
|
83
|
-
|
|
84
|
-
|
|
98
|
+
- **Addresses** are limited to \`http\`, \`https\`, \`mailto\` and \`cid\`, wherever
|
|
99
|
+
they appear. That includes inside a CSS value: \`background: url(…)\` is an
|
|
100
|
+
address like any other, it is listed on the preview page like any other, and a
|
|
101
|
+
\`data:\` one is refused there too. A \`data:\` address would carry a whole file
|
|
102
|
+
inside the markup, where nothing lists it as an attachment.
|
|
103
|
+
- **Comments are refused**, both levels, ordinary ones included. A comment looks
|
|
104
|
+
inert and is not: Outlook runs conditional comments, so markup inside one
|
|
105
|
+
reaches some recipients as markup and everyone else as nothing, and neither
|
|
106
|
+
the confirmation nor the preview can show what the first group sees. Write the
|
|
107
|
+
message without them; a recipient gains nothing from a comment.
|
|
85
108
|
|
|
86
109
|
## What the person confirming is shown
|
|
87
110
|
|
|
88
|
-
|
|
111
|
+
Two things, in two places, and knowing which is which explains most of the rules
|
|
112
|
+
above.
|
|
113
|
+
|
|
114
|
+
**In the client**, a short question: who it is from, **every** recipient, the
|
|
115
|
+
subject, one line of counts, and a link. No part of the message itself. It used
|
|
116
|
+
to carry the body and every address, and grew to 74 lines for a message with
|
|
117
|
+
eighteen links, at which point the confirm button sat below the bottom of the
|
|
118
|
+
dialog and nothing could be sent at all. Recipients are the one thing never
|
|
119
|
+
summarised, however many there are.
|
|
89
120
|
|
|
90
|
-
|
|
121
|
+
**In the browser**, everything else:
|
|
122
|
+
|
|
123
|
+
- The message rendered as the recipient will see it, quote included.
|
|
91
124
|
- **Every address in it, in full**, links and images alike, each with the text it
|
|
92
125
|
is shown as. A link's visible text and its target are the same string in plain
|
|
93
126
|
text and two different strings in markup, and that difference is the shape of
|
|
94
127
|
every phishing message ever written.
|
|
95
|
-
- **Every piece of text a recipient can read that the body
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
128
|
+
- **Every piece of text a recipient can read that the body does not show.** An
|
|
129
|
+
image's alt text is the clearest case: it never appears in the converted body,
|
|
130
|
+
and mail clients block remote images by default, so it is frequently what the
|
|
131
|
+
recipient actually reads.
|
|
99
132
|
- Every file carried with the message, with its size.
|
|
133
|
+
- Every property used beyond ordinary formatting, if \`markupLevel\` was
|
|
134
|
+
\`extended\`, with the ones that really hide something marked as such.
|
|
135
|
+
|
|
136
|
+
The answer is given in the client, never in the browser. That page has no
|
|
137
|
+
confirm button: its address reaches you through \`open_configuration\`, so a
|
|
138
|
+
control there would be one you could press yourself.
|
|
100
139
|
|
|
101
140
|
The agreement is bound to that exact message by a fingerprint covering the
|
|
102
141
|
sender, every recipient, the subject, the markup and the contents of each
|
|
@@ -130,8 +169,16 @@ in the thread. If threading matters, use \`send_reply\` rather than
|
|
|
130
169
|
|
|
131
170
|
A message cannot be given a file from this machine. That is a decision that has
|
|
132
171
|
not been made rather than a feature that is missing: which paths a model may
|
|
133
|
-
reach is its own question.
|
|
134
|
-
|
|
172
|
+
reach is its own question.
|
|
173
|
+
|
|
174
|
+
**This does not stop a message from showing images.** An image at a web address
|
|
175
|
+
needs no attachment at all, and that is how almost every formatted message does
|
|
176
|
+
it. Only a file that would travel inside the message is unavailable.
|
|
177
|
+
|
|
178
|
+
\`cid:\` addresses are permitted and refer to a file the message already carries.
|
|
179
|
+
When composing a new message there is none, so a \`cid:\` reference has nothing
|
|
180
|
+
to point at. Where they do work is a forward: the original travels along whole,
|
|
181
|
+
attachments and embedded images included, so nothing is lost there.
|
|
135
182
|
`;
|
|
136
183
|
const BRIDGE_GUIDE = `# How the Bridge behaves
|
|
137
184
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guidance.js","sourceRoot":"","sources":["../../src/tools/guidance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,wFAAwF;IACxF,EAAE;IACF,wFAAwF;IACxF,gBAAgB;IAChB,EAAE;IACF,4FAA4F;IAC5F,0FAA0F;IAC1F,yCAAyC;IACzC,4FAA4F;IAC5F,uFAAuF;IACvF,6DAA6D;IAC7D,oFAAoF;IACpF,2FAA2F;IAC3F,4FAA4F;IAC5F,kCAAkC;IAClC,uEAAuE;IACvE,EAAE;IACF,8CAA8C;IAC9C,kFAAkF;IAClF,mDAAmD;IACnD,yFAAyF;IACzF,8CAA8C;CAC/C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAEZ,MAAM,aAAa,GAAG
|
|
1
|
+
{"version":3,"file":"guidance.js","sourceRoot":"","sources":["../../src/tools/guidance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,wFAAwF;IACxF,EAAE;IACF,wFAAwF;IACxF,gBAAgB;IAChB,EAAE;IACF,4FAA4F;IAC5F,0FAA0F;IAC1F,yCAAyC;IACzC,4FAA4F;IAC5F,uFAAuF;IACvF,6DAA6D;IAC7D,oFAAoF;IACpF,2FAA2F;IAC3F,4FAA4F;IAC5F,kCAAkC;IAClC,uEAAuE;IACvE,EAAE;IACF,0FAA0F;IAC1F,6FAA6F;IAC7F,yFAAyF;IACzF,EAAE;IACF,8CAA8C;IAC9C,kFAAkF;IAClF,mDAAmD;IACnD,yFAAyF;IACzF,8CAA8C;CAC/C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAEZ,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6HrB,CAAA;AAED,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkEpB,CAAA;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAiB;IAChD,MAAM,CAAC,gBAAgB,CACrB,eAAe,EACf,4BAA4B,EAC5B;QACE,KAAK,EAAE,mCAAmC;QAC1C,WAAW,EACT,yFAAyF;YACzF,2FAA2F;YAC3F,4EAA4E;QAC9E,QAAQ,EAAE,eAAe;KAC1B,EACD,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC,EAAE,CAAC,CACnG,CAAA;IAED,MAAM,CAAC,gBAAgB,CACrB,cAAc,EACd,2BAA2B,EAC3B;QACE,KAAK,EAAE,oCAAoC;QAC3C,WAAW,EACT,qFAAqF;YACrF,0FAA0F;YAC1F,WAAW;QACb,QAAQ,EAAE,eAAe;KAC1B,EACD,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC,EAAE,CAAC,CAClG,CAAA;AACH,CAAC"}
|
package/dist/tools/interface.js
CHANGED
|
@@ -21,9 +21,13 @@ export function registerInterfaceTool(server, info) {
|
|
|
21
21
|
server.registerTool('open_configuration', {
|
|
22
22
|
title: 'Open the configuration interface',
|
|
23
23
|
description: 'Returns the address of the local configuration interface, which runs for as long as this ' +
|
|
24
|
-
'server does
|
|
25
|
-
'
|
|
26
|
-
'
|
|
24
|
+
'server does and is where everything about this server can be seen and changed. Give it ' +
|
|
25
|
+
'to the user whenever they ask for the interface, the panel, the settings, the web page, ' +
|
|
26
|
+
'the address or the link, however they phrase it, and also when they ask where to sign ' +
|
|
27
|
+
'in, where to see what has been sent, where the folders are listed, or when another tool ' +
|
|
28
|
+
'reports that no credentials are available. The address contains an access token and ' +
|
|
29
|
+
'changes every time the server restarts, so it cannot be remembered from an earlier ' +
|
|
30
|
+
'conversation and has to be asked for again.',
|
|
27
31
|
inputSchema: z.object({}),
|
|
28
32
|
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
29
33
|
}, async () => track(async () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interface.js","sourceRoot":"","sources":["../../src/tools/interface.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAGxB,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAC5D,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAA;AAYvC,MAAM,UAAU,qBAAqB,CAAC,MAAiB,EAAE,IAAmB;IAC1E,MAAM,CAAC,YAAY,CACjB,oBAAoB,EACpB;QACE,KAAK,EAAE,kCAAkC;QACzC,WAAW,EACT,2FAA2F;YAC3F,
|
|
1
|
+
{"version":3,"file":"interface.js","sourceRoot":"","sources":["../../src/tools/interface.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAGxB,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAC5D,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAA;AAYvC,MAAM,UAAU,qBAAqB,CAAC,MAAiB,EAAE,IAAmB;IAC1E,MAAM,CAAC,YAAY,CACjB,oBAAoB,EACpB;QACE,KAAK,EAAE,kCAAkC;QACzC,WAAW,EACT,2FAA2F;YAC3F,yFAAyF;YACzF,0FAA0F;YAC1F,wFAAwF;YACxF,0FAA0F;YAC1F,sFAAsF;YACtF,qFAAqF;YACrF,6CAA6C;QAC/C,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QACzB,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE;KAC1D,EACD,KAAK,IAAI,EAAE,CACT,KAAK,CAAC,KAAK,IAAI,EAAE;QACf,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;YACpB,MAAM,OAAO,GAAmB;gBAC9B,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EACF,8EAA8E;4BAC9E,kFAAkF;4BAClF,4EAA4E;4BAC5E,oCAAoC;qBACvC;iBACF;gBACD,OAAO,EAAE,IAAI;aACd,CAAA;YACD,OAAO,OAAO,CAAA;QAChB,CAAC;QAED,MAAM,KAAK,GAAa,CAAC,4BAA4B,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,CAAC,CAAA;QAEtE,IAAI,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC;YACpB,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,CAAA;YAC7B,KAAK,CAAC,IAAI,CACR,gBAAgB,IAAI,CAAC,OAAO,EAAE,GAAG,EACjC,IAAI;gBACF,CAAC,CAAC,4BAA4B,kBAAkB,CAAC,IAAI,CAAC,CAAC,KAAK,GAAG;gBAC/D,CAAC,CAAC,6CAA6C,EACjD,EAAE,EACF,iEAAiE,CAClE,CAAA;QACH,CAAC;aAAM,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC;YAC5C,KAAK,CAAC,IAAI,CACR,uFAAuF,EACvF,qFAAqF,EACrF,qCAAqC,EACrC,EAAE,EACF,oFAAoF,CACrF,CAAA;QACH,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CACR,oFAAoF,EACpF,qFAAqF,EACrF,8BAA8B,EAC9B,EAAE,EACF,uFAAuF,EACvF,6EAA6E,CAC9E,CAAA;QACH,CAAC;QAED,KAAK,CAAC,IAAI,CACR,EAAE,EACF,yFAAyF,CAC1F,CAAA;QAED,MAAM,MAAM,GAAmB,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAA;QACtF,OAAO,MAAM,CAAA;IACf,CAAC,EAAE,oBAAoB,CAAC,CAC3B,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turning a held message into what the preview page needs.
|
|
3
|
+
*
|
|
4
|
+
* Its own module rather than a method on either side, because it is the one
|
|
5
|
+
* place where the two halves meet: the sending path knows what a Draft is, the
|
|
6
|
+
* interface knows what it can render, and neither should have to know the
|
|
7
|
+
* other. Putting the translation here keeps `src/web` free of mail types and
|
|
8
|
+
* the sending path free of page structure.
|
|
9
|
+
*
|
|
10
|
+
* Everything the confirmation used to say is assembled here instead, from the
|
|
11
|
+
* same functions that said it. Nothing was dropped in the move: the address
|
|
12
|
+
* list is still every address, the hidden text is still every alt text and
|
|
13
|
+
* title, and the quote is still counted rather than listed for the reason
|
|
14
|
+
* written at summariseQuote.
|
|
15
|
+
*/
|
|
16
|
+
/** What the page needs, without the parts the interface fills in itself. */
|
|
17
|
+
export interface PendingView {
|
|
18
|
+
tool: string;
|
|
19
|
+
from: string;
|
|
20
|
+
to: string[];
|
|
21
|
+
cc: string[];
|
|
22
|
+
bcc: string[];
|
|
23
|
+
subject: string;
|
|
24
|
+
html?: string;
|
|
25
|
+
text?: string;
|
|
26
|
+
urls: Array<{
|
|
27
|
+
url: string;
|
|
28
|
+
label: string;
|
|
29
|
+
kind: 'link' | 'image' | 'style';
|
|
30
|
+
}>;
|
|
31
|
+
hiddenText: string[];
|
|
32
|
+
attachments: string[];
|
|
33
|
+
quoted?: {
|
|
34
|
+
links: number;
|
|
35
|
+
images: number;
|
|
36
|
+
hiddenText: number;
|
|
37
|
+
};
|
|
38
|
+
styleNotes: Array<{
|
|
39
|
+
property: string;
|
|
40
|
+
value: string;
|
|
41
|
+
element: string;
|
|
42
|
+
hides: boolean;
|
|
43
|
+
}>;
|
|
44
|
+
}
|
|
45
|
+
/** Assembles the view for a message that is waiting, if one is. */
|
|
46
|
+
export declare function pendingView(digest: string): PendingView | undefined;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turning a held message into what the preview page needs.
|
|
3
|
+
*
|
|
4
|
+
* Its own module rather than a method on either side, because it is the one
|
|
5
|
+
* place where the two halves meet: the sending path knows what a Draft is, the
|
|
6
|
+
* interface knows what it can render, and neither should have to know the
|
|
7
|
+
* other. Putting the translation here keeps `src/web` free of mail types and
|
|
8
|
+
* the sending path free of page structure.
|
|
9
|
+
*
|
|
10
|
+
* Everything the confirmation used to say is assembled here instead, from the
|
|
11
|
+
* same functions that said it. Nothing was dropped in the move: the address
|
|
12
|
+
* list is still every address, the hidden text is still every alt text and
|
|
13
|
+
* title, and the quote is still counted rather than listed for the reason
|
|
14
|
+
* written at summariseQuote.
|
|
15
|
+
*/
|
|
16
|
+
import { composedBody, describeAttachments, describeUrls, hiddenTextOf, summariseQuote, styleNotesOf, showRecipient, } from '../mail/compose.js';
|
|
17
|
+
import { previewOf } from './preview.js';
|
|
18
|
+
/**
|
|
19
|
+
* The message as it would arrive, quote and all.
|
|
20
|
+
*
|
|
21
|
+
* The preview shows the whole thing rather than only the composed part, which
|
|
22
|
+
* is the opposite of what the confirmation does. Both are right for where they
|
|
23
|
+
* are: the question in the client asks about the part somebody wrote, while
|
|
24
|
+
* this page answers "what does the recipient get", and for a reply that
|
|
25
|
+
* includes the quote.
|
|
26
|
+
*/
|
|
27
|
+
function bodyFor(draft) {
|
|
28
|
+
if (draft.html !== undefined) {
|
|
29
|
+
return { html: `${draft.html}${draft.quotedHtml ?? ''}` };
|
|
30
|
+
}
|
|
31
|
+
return { text: composedBody(draft) };
|
|
32
|
+
}
|
|
33
|
+
/** Assembles the view for a message that is waiting, if one is. */
|
|
34
|
+
export function pendingView(digest) {
|
|
35
|
+
const waiting = previewOf(digest);
|
|
36
|
+
if (!waiting)
|
|
37
|
+
return undefined;
|
|
38
|
+
const { draft, tool } = waiting;
|
|
39
|
+
const quote = summariseQuote(draft);
|
|
40
|
+
return {
|
|
41
|
+
tool,
|
|
42
|
+
from: showRecipient(draft.from),
|
|
43
|
+
to: draft.to.map(showRecipient),
|
|
44
|
+
cc: draft.cc.map(showRecipient),
|
|
45
|
+
bcc: draft.bcc.map(showRecipient),
|
|
46
|
+
subject: draft.subject,
|
|
47
|
+
...bodyFor(draft),
|
|
48
|
+
urls: describeUrls(draft).map((u) => ({ url: u.url, label: u.label, kind: u.kind })),
|
|
49
|
+
hiddenText: hiddenTextOf(draft),
|
|
50
|
+
attachments: describeAttachments(draft),
|
|
51
|
+
styleNotes: styleNotesOf(draft),
|
|
52
|
+
...(quote
|
|
53
|
+
? {
|
|
54
|
+
quoted: {
|
|
55
|
+
links: quote.links,
|
|
56
|
+
images: quote.images,
|
|
57
|
+
hiddenText: quote.hiddenText.length,
|
|
58
|
+
},
|
|
59
|
+
}
|
|
60
|
+
: {}),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=pending-view.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pending-view.js","sourceRoot":"","sources":["../../src/tools/pending-view.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,aAAa,GAEd,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAmBxC;;;;;;;;GAQG;AACH,SAAS,OAAO,CAAC,KAAY;IAC3B,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,UAAU,IAAI,EAAE,EAAE,EAAE,CAAA;IAC3D,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,YAAY,CAAC,KAAK,CAAC,EAAE,CAAA;AACtC,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,CAAC,CAAA;IACjC,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAA;IAE9B,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAA;IAC/B,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,CAAC,CAAA;IAEnC,OAAO;QACL,IAAI;QACJ,IAAI,EAAE,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC;QAC/B,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,aAAa,CAAC;QAC/B,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,aAAa,CAAC;QAC/B,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,aAAa,CAAC;QACjC,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,GAAG,OAAO,CAAC,KAAK,CAAC;QACjB,IAAI,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACpF,UAAU,EAAE,YAAY,CAAC,KAAK,CAAC;QAC/B,WAAW,EAAE,mBAAmB,CAAC,KAAK,CAAC;QACvC,UAAU,EAAE,YAAY,CAAC,KAAK,CAAC;QAC/B,GAAG,CAAC,KAAK;YACP,CAAC,CAAC;gBACE,MAAM,EAAE;oBACN,KAAK,EAAE,KAAK,CAAC,KAAK;oBAClB,MAAM,EAAE,KAAK,CAAC,MAAM;oBACpB,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,MAAM;iBACpC;aACF;YACH,CAAC,CAAC,EAAE,CAAC;KACR,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The message a confirmation is about, held while the question is open.
|
|
3
|
+
*
|
|
4
|
+
* The confirmation itself carries no message content any more. It names the
|
|
5
|
+
* sender, the recipients and the subject, and points at a page in the local
|
|
6
|
+
* interface where the message can be read as the recipient will see it. That
|
|
7
|
+
* page needs the composed message, and this is where it waits.
|
|
8
|
+
*
|
|
9
|
+
* ## Why it has to be held at all
|
|
10
|
+
*
|
|
11
|
+
* The sending path builds the draft twice: once to ask, once to send. Nothing
|
|
12
|
+
* is kept between the two rounds except a sealed digest, which is what makes
|
|
13
|
+
* the confirmation impossible to forge. That design is untouched. A preview,
|
|
14
|
+
* though, has to be readable *between* the rounds, from a different process
|
|
15
|
+
* boundary entirely, so the message has to sit somewhere for as long as the
|
|
16
|
+
* question is unanswered.
|
|
17
|
+
*
|
|
18
|
+
* ## The rules that follow from what this holds
|
|
19
|
+
*
|
|
20
|
+
* This is decrypted mail. It is the one thing the project has always refused to
|
|
21
|
+
* put on disk, because doing so would undo Proton's encryption locally. So:
|
|
22
|
+
*
|
|
23
|
+
* - **Memory only.** Nothing here is ever written anywhere.
|
|
24
|
+
* - **Dropped as soon as the answer arrives**, whichever way it went.
|
|
25
|
+
* - **Dropped when it goes stale**, because a client that never answers must
|
|
26
|
+
* not leave a message readable for the life of the process.
|
|
27
|
+
* - **Bounded**, so a caller that asks a thousand times and answers none of
|
|
28
|
+
* them cannot fill memory.
|
|
29
|
+
*
|
|
30
|
+
* Addressed by the digest that already binds the confirmation. That is not a
|
|
31
|
+
* shortcut: it means a preview can only be found by somebody who knows exactly
|
|
32
|
+
* which message was composed, and it makes the preview and the thing being
|
|
33
|
+
* confirmed the same object by construction.
|
|
34
|
+
*/
|
|
35
|
+
import type { Draft } from '../mail/compose.js';
|
|
36
|
+
/** How long an unanswered question keeps its preview readable. */
|
|
37
|
+
export declare const PREVIEW_TTL_MS: number;
|
|
38
|
+
/**
|
|
39
|
+
* How many may wait at once.
|
|
40
|
+
*
|
|
41
|
+
* Low on purpose. A person answers one question at a time; anything beyond a
|
|
42
|
+
* handful means something is asking without waiting for answers, and the
|
|
43
|
+
* oldest are the ones nobody is coming back for.
|
|
44
|
+
*/
|
|
45
|
+
export declare const MAX_PREVIEWS = 8;
|
|
46
|
+
/** For tests only. */
|
|
47
|
+
export declare function _setClock(clock: () => number): void;
|
|
48
|
+
/** For tests only. */
|
|
49
|
+
export declare function _reset(): void;
|
|
50
|
+
/** Holds a message for as long as its question is open. */
|
|
51
|
+
export declare function holdForPreview(digest: string, draft: Draft, tool: string): void;
|
|
52
|
+
/** The message behind a digest, if the question is still open. */
|
|
53
|
+
export declare function previewOf(digest: string): {
|
|
54
|
+
draft: Draft;
|
|
55
|
+
tool: string;
|
|
56
|
+
} | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* Forgets a message.
|
|
59
|
+
*
|
|
60
|
+
* Called once the answer is in, whichever way it went. A declined send has no
|
|
61
|
+
* more business being readable than a completed one.
|
|
62
|
+
*/
|
|
63
|
+
export declare function releasePreview(digest: string): void;
|
|
64
|
+
/**
|
|
65
|
+
* How many are waiting.
|
|
66
|
+
*
|
|
67
|
+
* Written for the interface to report rather than guess, and not wired up
|
|
68
|
+
* there yet: nothing under src/web calls this today.
|
|
69
|
+
*/
|
|
70
|
+
export declare function pendingCount(): number;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The message a confirmation is about, held while the question is open.
|
|
3
|
+
*
|
|
4
|
+
* The confirmation itself carries no message content any more. It names the
|
|
5
|
+
* sender, the recipients and the subject, and points at a page in the local
|
|
6
|
+
* interface where the message can be read as the recipient will see it. That
|
|
7
|
+
* page needs the composed message, and this is where it waits.
|
|
8
|
+
*
|
|
9
|
+
* ## Why it has to be held at all
|
|
10
|
+
*
|
|
11
|
+
* The sending path builds the draft twice: once to ask, once to send. Nothing
|
|
12
|
+
* is kept between the two rounds except a sealed digest, which is what makes
|
|
13
|
+
* the confirmation impossible to forge. That design is untouched. A preview,
|
|
14
|
+
* though, has to be readable *between* the rounds, from a different process
|
|
15
|
+
* boundary entirely, so the message has to sit somewhere for as long as the
|
|
16
|
+
* question is unanswered.
|
|
17
|
+
*
|
|
18
|
+
* ## The rules that follow from what this holds
|
|
19
|
+
*
|
|
20
|
+
* This is decrypted mail. It is the one thing the project has always refused to
|
|
21
|
+
* put on disk, because doing so would undo Proton's encryption locally. So:
|
|
22
|
+
*
|
|
23
|
+
* - **Memory only.** Nothing here is ever written anywhere.
|
|
24
|
+
* - **Dropped as soon as the answer arrives**, whichever way it went.
|
|
25
|
+
* - **Dropped when it goes stale**, because a client that never answers must
|
|
26
|
+
* not leave a message readable for the life of the process.
|
|
27
|
+
* - **Bounded**, so a caller that asks a thousand times and answers none of
|
|
28
|
+
* them cannot fill memory.
|
|
29
|
+
*
|
|
30
|
+
* Addressed by the digest that already binds the confirmation. That is not a
|
|
31
|
+
* shortcut: it means a preview can only be found by somebody who knows exactly
|
|
32
|
+
* which message was composed, and it makes the preview and the thing being
|
|
33
|
+
* confirmed the same object by construction.
|
|
34
|
+
*/
|
|
35
|
+
/** How long an unanswered question keeps its preview readable. */
|
|
36
|
+
export const PREVIEW_TTL_MS = 15 * 60 * 1000;
|
|
37
|
+
/**
|
|
38
|
+
* How many may wait at once.
|
|
39
|
+
*
|
|
40
|
+
* Low on purpose. A person answers one question at a time; anything beyond a
|
|
41
|
+
* handful means something is asking without waiting for answers, and the
|
|
42
|
+
* oldest are the ones nobody is coming back for.
|
|
43
|
+
*/
|
|
44
|
+
export const MAX_PREVIEWS = 8;
|
|
45
|
+
const held = new Map();
|
|
46
|
+
/**
|
|
47
|
+
* The clock, replaceable for tests.
|
|
48
|
+
*
|
|
49
|
+
* A test for expiry that waits fifteen minutes is a test nobody runs.
|
|
50
|
+
*/
|
|
51
|
+
let now = () => Date.now();
|
|
52
|
+
/** For tests only. */
|
|
53
|
+
export function _setClock(clock) {
|
|
54
|
+
now = clock;
|
|
55
|
+
}
|
|
56
|
+
/** For tests only. */
|
|
57
|
+
export function _reset() {
|
|
58
|
+
held.clear();
|
|
59
|
+
now = () => Date.now();
|
|
60
|
+
}
|
|
61
|
+
function dropStale() {
|
|
62
|
+
const cutoff = now() - PREVIEW_TTL_MS;
|
|
63
|
+
for (const [digest, entry] of held) {
|
|
64
|
+
if (entry.at < cutoff)
|
|
65
|
+
held.delete(digest);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/** Holds a message for as long as its question is open. */
|
|
69
|
+
export function holdForPreview(digest, draft, tool) {
|
|
70
|
+
dropStale();
|
|
71
|
+
// Oldest first, since a Map keeps insertion order. The one nobody answered
|
|
72
|
+
// longest ago is the one least likely to be answered now.
|
|
73
|
+
while (held.size >= MAX_PREVIEWS) {
|
|
74
|
+
const oldest = held.keys().next();
|
|
75
|
+
if (oldest.done)
|
|
76
|
+
break;
|
|
77
|
+
held.delete(oldest.value);
|
|
78
|
+
}
|
|
79
|
+
held.set(digest, { draft, tool, at: now() });
|
|
80
|
+
}
|
|
81
|
+
/** The message behind a digest, if the question is still open. */
|
|
82
|
+
export function previewOf(digest) {
|
|
83
|
+
dropStale();
|
|
84
|
+
const entry = held.get(digest);
|
|
85
|
+
if (!entry)
|
|
86
|
+
return undefined;
|
|
87
|
+
return { draft: entry.draft, tool: entry.tool };
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Forgets a message.
|
|
91
|
+
*
|
|
92
|
+
* Called once the answer is in, whichever way it went. A declined send has no
|
|
93
|
+
* more business being readable than a completed one.
|
|
94
|
+
*/
|
|
95
|
+
export function releasePreview(digest) {
|
|
96
|
+
held.delete(digest);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* How many are waiting.
|
|
100
|
+
*
|
|
101
|
+
* Written for the interface to report rather than guess, and not wired up
|
|
102
|
+
* there yet: nothing under src/web calls this today.
|
|
103
|
+
*/
|
|
104
|
+
export function pendingCount() {
|
|
105
|
+
dropStale();
|
|
106
|
+
return held.size;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=preview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview.js","sourceRoot":"","sources":["../../src/tools/preview.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAIH,kEAAkE;AAClE,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;AAE5C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAA;AAS7B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAgB,CAAA;AAEpC;;;;GAIG;AACH,IAAI,GAAG,GAAiB,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAA;AAExC,sBAAsB;AACtB,MAAM,UAAU,SAAS,CAAC,KAAmB;IAC3C,GAAG,GAAG,KAAK,CAAA;AACb,CAAC;AAED,sBAAsB;AACtB,MAAM,UAAU,MAAM;IACpB,IAAI,CAAC,KAAK,EAAE,CAAA;IACZ,GAAG,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAA;AACxB,CAAC;AAED,SAAS,SAAS;IAChB,MAAM,MAAM,GAAG,GAAG,EAAE,GAAG,cAAc,CAAA;IACrC,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC;QACnC,IAAI,KAAK,CAAC,EAAE,GAAG,MAAM;YAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAC5C,CAAC;AACH,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,cAAc,CAAC,MAAc,EAAE,KAAY,EAAE,IAAY;IACvE,SAAS,EAAE,CAAA;IACX,2EAA2E;IAC3E,0DAA0D;IAC1D,OAAO,IAAI,CAAC,IAAI,IAAI,YAAY,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAA;QACjC,IAAI,MAAM,CAAC,IAAI;YAAE,MAAK;QACtB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IAC3B,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,CAAA;AAC9C,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,SAAS,CAAC,MAAc;IACtC,SAAS,EAAE,CAAA;IACX,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IAC9B,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAA;IAC5B,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAA;AACjD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;AACrB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY;IAC1B,SAAS,EAAE,CAAA;IACX,OAAO,IAAI,CAAC,IAAI,CAAA;AAClB,CAAC"}
|
package/dist/tools/send.js
CHANGED
|
@@ -19,6 +19,8 @@ import { sendMessage } from '../mail/send.js';
|
|
|
19
19
|
import { buildReplyDraft, buildForwardDraft, readDraftForSending, DRAFTS, } from '../mail/drafts.js';
|
|
20
20
|
import { describeRecipients, mintMessageId, parseRecipient, parseRecipients, MARKUP_NOTE, PLAIN_TEXT_NOTE, UTF8_NOTE, } from '../mail/compose.js';
|
|
21
21
|
import { clientCanConfirm, confirmationAnswer, confirmationRequest, digestOf, pendingSend, refuse, } from './confirm.js';
|
|
22
|
+
import { holdForPreview, releasePreview } from './preview.js';
|
|
23
|
+
import { previewUrl } from './failures.js';
|
|
22
24
|
import { describeFailure, withSignIn } from './failures.js';
|
|
23
25
|
import { track } from '../in-flight.js';
|
|
24
26
|
const ok = (text) => ({ content: [{ type: 'text', text }] });
|
|
@@ -86,10 +88,18 @@ async function confirmed(deps, ctx, tool, what, build) {
|
|
|
86
88
|
if (!clientCanConfirm(ctx))
|
|
87
89
|
return refuse.noElicitation();
|
|
88
90
|
const state = await deps.codec.mint({ tool, digest }, ctx);
|
|
89
|
-
|
|
91
|
+
// Held so the interface can show the message while the question is open.
|
|
92
|
+
// Memory only, and dropped the moment an answer arrives either way.
|
|
93
|
+
holdForPreview(digest, draft, tool);
|
|
94
|
+
return confirmationRequest(draft, what, state, previewUrl(digest));
|
|
90
95
|
}
|
|
91
96
|
// Second round. The seal has already been verified by the SDK, so what is
|
|
92
97
|
// left is whether it says the same thing this call is asking for.
|
|
98
|
+
//
|
|
99
|
+
// The preview is released before any of those checks, and before the send.
|
|
100
|
+
// A refused confirmation has no more business staying readable than a
|
|
101
|
+
// completed one, and an early return must not leave decrypted mail behind.
|
|
102
|
+
releasePreview(digest);
|
|
93
103
|
if (pending.tool !== tool)
|
|
94
104
|
return refuse.wrongTool();
|
|
95
105
|
if (pending.digest !== digest)
|
|
@@ -103,9 +113,11 @@ const addressList = (what) => z
|
|
|
103
113
|
.array(z.string())
|
|
104
114
|
.optional()
|
|
105
115
|
.describe(`${what} Each entry is an address, optionally as "Display Name <name@example.com>".`);
|
|
106
|
-
const CONFIRMATION_NOTE = 'Every send asks the user to confirm first, through the client, showing the
|
|
107
|
-
'
|
|
108
|
-
'
|
|
116
|
+
const CONFIRMATION_NOTE = 'Every send asks the user to confirm first, through the client, showing the sender, every ' +
|
|
117
|
+
'recipient separated into To, Cc and Bcc, and the subject. It carries no part of the message ' +
|
|
118
|
+
'itself and links to a page where the whole thing can be read as the recipient will see it. ' +
|
|
119
|
+
'There is no way to switch that off, and a client that cannot show the question cannot send ' +
|
|
120
|
+
'at all.';
|
|
109
121
|
export function registerSendTools(server, deps) {
|
|
110
122
|
server.registerTool('send_message', {
|
|
111
123
|
title: 'Send a message',
|
|
@@ -124,6 +136,14 @@ export function registerSendTools(server, deps) {
|
|
|
124
136
|
'links and images, with colour, font, alignment, spacing and borders. Images may ' +
|
|
125
137
|
'point at a full address; anything outside the permitted set is refused with a ' +
|
|
126
138
|
'reason rather than removed.'),
|
|
139
|
+
markupLevel: z
|
|
140
|
+
.enum(['standard', 'extended'])
|
|
141
|
+
.default('standard')
|
|
142
|
+
.describe('How much markup is permitted. "standard" covers ordinary formatted mail. ' +
|
|
143
|
+
'"extended" permits every CSS property, including ones that can hide content, and ' +
|
|
144
|
+
'makes the confirmation carry a warning telling the person to open the preview. ' +
|
|
145
|
+
'Prefer "standard"; reach for "extended" only when asked for something that needs ' +
|
|
146
|
+
'it, such as a button, which requires display:inline-block.'),
|
|
127
147
|
}),
|
|
128
148
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
|
|
129
149
|
}, async (input, ctx) => track(() => withSignIn(ctx, async () => {
|
|
@@ -145,7 +165,9 @@ export function registerSendTools(server, deps) {
|
|
|
145
165
|
// read `text` and get something true.
|
|
146
166
|
text: input.html !== undefined ? htmlToText(input.html) : input.text,
|
|
147
167
|
messageId: mintMessageId(from.address),
|
|
148
|
-
...(input.html !== undefined
|
|
168
|
+
...(input.html !== undefined
|
|
169
|
+
? { html: input.html, markupLevel: input.markupLevel }
|
|
170
|
+
: {}),
|
|
149
171
|
};
|
|
150
172
|
});
|
|
151
173
|
}
|
|
@@ -170,9 +192,19 @@ export function registerSendTools(server, deps) {
|
|
|
170
192
|
.string()
|
|
171
193
|
.optional()
|
|
172
194
|
.describe('Your part of the message as markup instead of plain text. The original is quoted ' +
|
|
173
|
-
'below it either way. The quote
|
|
174
|
-
'
|
|
175
|
-
'
|
|
195
|
+
'below it either way. The quote keeps the original\'s own markup, character for ' +
|
|
196
|
+
'character, minus what would reach out of it: style blocks, document-level ' +
|
|
197
|
+
'elements, scripts and comments. A message this server would not itself compose ' +
|
|
198
|
+
'can therefore still be replied to or forwarded, but its formatting travels ' +
|
|
199
|
+
'along, and so does anything a link in it points at.'),
|
|
200
|
+
markupLevel: z
|
|
201
|
+
.enum(['standard', 'extended'])
|
|
202
|
+
.default('standard')
|
|
203
|
+
.describe('How much markup is permitted. "standard" covers ordinary formatted mail. ' +
|
|
204
|
+
'"extended" permits every CSS property, including ones that can hide content, and ' +
|
|
205
|
+
'makes the confirmation carry a warning telling the person to open the preview. ' +
|
|
206
|
+
'Prefer "standard"; reach for "extended" only when asked for something that needs ' +
|
|
207
|
+
'it, such as a button, which requires display:inline-block.'),
|
|
176
208
|
mailbox: z.string().optional().describe('The mailbox the original is in, if known.'),
|
|
177
209
|
}),
|
|
178
210
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
|
|
@@ -200,9 +232,19 @@ export function registerSendTools(server, deps) {
|
|
|
200
232
|
.string()
|
|
201
233
|
.optional()
|
|
202
234
|
.describe('Your part of the message as markup instead of plain text. The original is quoted ' +
|
|
203
|
-
'below it either way. The quote
|
|
204
|
-
'
|
|
205
|
-
'
|
|
235
|
+
'below it either way. The quote keeps the original\'s own markup, character for ' +
|
|
236
|
+
'character, minus what would reach out of it: style blocks, document-level ' +
|
|
237
|
+
'elements, scripts and comments. A message this server would not itself compose ' +
|
|
238
|
+
'can therefore still be replied to or forwarded, but its formatting travels ' +
|
|
239
|
+
'along, and so does anything a link in it points at.'),
|
|
240
|
+
markupLevel: z
|
|
241
|
+
.enum(['standard', 'extended'])
|
|
242
|
+
.default('standard')
|
|
243
|
+
.describe('How much markup is permitted. "standard" covers ordinary formatted mail. ' +
|
|
244
|
+
'"extended" permits every CSS property, including ones that can hide content, and ' +
|
|
245
|
+
'makes the confirmation carry a warning telling the person to open the preview. ' +
|
|
246
|
+
'Prefer "standard"; reach for "extended" only when asked for something that needs ' +
|
|
247
|
+
'it, such as a button, which requires display:inline-block.'),
|
|
206
248
|
mailbox: z.string().optional().describe('The mailbox the original is in, if known.'),
|
|
207
249
|
}),
|
|
208
250
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
|