@rubytech/create-maxy-code 0.1.458 → 0.1.459
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/package.json +1 -1
- package/payload/platform/lib/storage-broker/dist/__tests__/cf-exec.test.js +188 -3
- package/payload/platform/lib/storage-broker/dist/__tests__/cf-exec.test.js.map +1 -1
- package/payload/platform/lib/storage-broker/dist/__tests__/object-limits.test.d.ts +2 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/object-limits.test.d.ts.map +1 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/object-limits.test.js +59 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/object-limits.test.js.map +1 -0
- package/payload/platform/lib/storage-broker/dist/cf-exec.d.ts +12 -1
- package/payload/platform/lib/storage-broker/dist/cf-exec.d.ts.map +1 -1
- package/payload/platform/lib/storage-broker/dist/cf-exec.js +97 -0
- package/payload/platform/lib/storage-broker/dist/cf-exec.js.map +1 -1
- package/payload/platform/lib/storage-broker/dist/house-credential.d.ts.map +1 -1
- package/payload/platform/lib/storage-broker/dist/house-credential.js +4 -2
- package/payload/platform/lib/storage-broker/dist/house-credential.js.map +1 -1
- package/payload/platform/lib/storage-broker/dist/index.d.ts +1 -0
- package/payload/platform/lib/storage-broker/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/storage-broker/dist/index.js +1 -0
- package/payload/platform/lib/storage-broker/dist/index.js.map +1 -1
- package/payload/platform/lib/storage-broker/dist/object-limits.d.ts +53 -0
- package/payload/platform/lib/storage-broker/dist/object-limits.d.ts.map +1 -0
- package/payload/platform/lib/storage-broker/dist/object-limits.js +72 -0
- package/payload/platform/lib/storage-broker/dist/object-limits.js.map +1 -0
- package/payload/platform/lib/storage-broker/src/__tests__/cf-exec.test.ts +211 -5
- package/payload/platform/lib/storage-broker/src/__tests__/object-limits.test.ts +76 -0
- package/payload/platform/lib/storage-broker/src/cf-exec.ts +148 -6
- package/payload/platform/lib/storage-broker/src/house-credential.ts +4 -2
- package/payload/platform/lib/storage-broker/src/index.ts +1 -0
- package/payload/platform/lib/storage-broker/src/object-limits.ts +75 -0
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +5 -5
- package/payload/platform/plugins/cloudflare/bin/cf-token.sh +4 -3
- package/payload/platform/plugins/cloudflare/references/api.md +1 -1
- package/payload/platform/plugins/docs/references/outlook-guide.md +4 -4
- package/payload/platform/plugins/outlook/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/outlook/PLUGIN.md +2 -2
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/attach.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/attach.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/attach.test.js +138 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/attach.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-attachments.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-attachments.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-attachments.test.js +121 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-attachments.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-edit-attachments.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-edit-attachments.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-edit-attachments.test.js +102 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-edit-attachments.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-edit.test.js +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-edit.test.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-send-attachments.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-send-attachments.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-send-attachments.test.js +209 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-send-attachments.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/index.js +10 -7
- package/payload/platform/plugins/outlook/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/lib/attach.d.ts +37 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/attach.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/attach.js +79 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/attach.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/message.d.ts +8 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/message.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/lib/message.js +4 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/message.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/lib/outbound-attachments.d.ts +31 -8
- package/payload/platform/plugins/outlook/mcp/dist/lib/outbound-attachments.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/lib/outbound-attachments.js +40 -13
- package/payload/platform/plugins/outlook/mcp/dist/lib/outbound-attachments.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/lib/upload-session.d.ts +9 -5
- package/payload/platform/plugins/outlook/mcp/dist/lib/upload-session.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/lib/upload-session.js +12 -8
- package/payload/platform/plugins/outlook/mcp/dist/lib/upload-session.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/draft-edit.d.ts +29 -4
- package/payload/platform/plugins/outlook/mcp/dist/tools/draft-edit.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/draft-edit.js +46 -13
- package/payload/platform/plugins/outlook/mcp/dist/tools/draft-edit.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/draft.d.ts +8 -2
- package/payload/platform/plugins/outlook/mcp/dist/tools/draft.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/draft.js +13 -4
- package/payload/platform/plugins/outlook/mcp/dist/tools/draft.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-reply.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-reply.js +3 -20
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-reply.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-send.d.ts +27 -3
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-send.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-send.js +53 -8
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-send.js.map +1 -1
- package/payload/platform/plugins/outlook/references/graph-surfaces.md +29 -6
- package/payload/platform/plugins/outlook/skills/outlook/SKILL.md +22 -5
- package/payload/platform/plugins/storage-broker/PLUGIN.md +41 -2
- package/payload/platform/plugins/storage-broker/mcp/dist/__tests__/confine-path.test.d.ts +2 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/__tests__/confine-path.test.d.ts.map +1 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/__tests__/confine-path.test.js +85 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/__tests__/confine-path.test.js.map +1 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/__tests__/put-source.test.d.ts +2 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/__tests__/put-source.test.d.ts.map +1 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/__tests__/put-source.test.js +77 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/__tests__/put-source.test.js.map +1 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/confine-path.d.ts +12 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/confine-path.d.ts.map +1 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/confine-path.js +107 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/confine-path.js.map +1 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/index.js +86 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/storage-broker/mcp/dist/put-source.d.ts +11 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/put-source.d.ts.map +1 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/put-source.js +35 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/put-source.js.map +1 -0
- package/payload/platform/scripts/logs-read-jsonl.test.sh +118 -0
- package/payload/platform/scripts/logs-read.sh +63 -9
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +4 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- package/payload/server/{chunk-F4D35LKL.js → chunk-POBXIHOF.js} +34 -0
- package/payload/server/{chunk-64FGYKNZ.js → chunk-Q6W4U6HL.js} +104 -0
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/server.js +206 -6
- package/payload/server/{src-S7C4P6TT.js → src-3I2RYZFB.js} +9 -1
|
@@ -1,18 +1,26 @@
|
|
|
1
1
|
import { postGraph } from "../lib/graph-client.js";
|
|
2
2
|
import { buildMessage } from "../lib/message.js";
|
|
3
|
+
import { attachFilesToDraft, resolveAttachmentsOrRefuse } from "../lib/attach.js";
|
|
3
4
|
import { log } from "../lib/log.js";
|
|
4
5
|
/**
|
|
5
|
-
* Create a draft via Graph `POST /me/messages
|
|
6
|
-
*
|
|
6
|
+
* Create a draft via Graph `POST /me/messages`, then attach any files to it, so
|
|
7
|
+
* the returned id names a Drafts-folder message that already carries them and is
|
|
8
|
+
* ready for outlook-draft-send.
|
|
7
9
|
*
|
|
8
10
|
* That id is durable only because the plugin opts into immutable ids
|
|
9
11
|
* (IMMUTABLE_ID_PREFER in graph-client). A regular Outlook id is NOT stable — it
|
|
10
12
|
* rotates when the item is moved or re-saved, including by the operator's own
|
|
11
13
|
* mail client, and the old value then 404s (Task 1688).
|
|
14
|
+
*
|
|
15
|
+
* Attachment paths validate BEFORE the create, so a bad path leaves no orphan
|
|
16
|
+
* draft. An attach failing Graph-side after the create does leave one; it is
|
|
17
|
+
* operator-visible in the Drafts folder — the same exposure mail-reply carries.
|
|
12
18
|
*/
|
|
13
19
|
export async function runDraft(config, input) {
|
|
14
20
|
const account = config.accountId;
|
|
15
21
|
log({ event: "draft-request", account });
|
|
22
|
+
// Before any Graph write: a bad path must leave nothing behind.
|
|
23
|
+
const files = resolveAttachmentsOrRefuse(account, input.attachments, "draft");
|
|
16
24
|
const message = buildMessage(input);
|
|
17
25
|
try {
|
|
18
26
|
const { json } = await postGraph(config, "/me/messages", message, { tool: "draft" });
|
|
@@ -20,8 +28,9 @@ export async function runDraft(config, input) {
|
|
|
20
28
|
if (!draftId) {
|
|
21
29
|
throw new Error("outlook-draft: Graph created the draft but returned no id.");
|
|
22
30
|
}
|
|
23
|
-
|
|
24
|
-
|
|
31
|
+
await attachFilesToDraft(config, draftId, files, "draft");
|
|
32
|
+
log({ event: "draft-created", account, id: draftId, attachments: files.length });
|
|
33
|
+
return { draftId, attachments: files.map((f) => f.name) };
|
|
25
34
|
}
|
|
26
35
|
catch (err) {
|
|
27
36
|
log({ event: "draft-failed", account, error: err.message });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"draft.js","sourceRoot":"","sources":["../../src/tools/draft.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAqB,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"draft.js","sourceRoot":"","sources":["../../src/tools/draft.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAqB,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAC;AAClF,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAOpC;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,MAAyB,EACzB,KAAmB;IAEnB,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC;IACjC,GAAG,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,CAAC,CAAC;IACzC,gEAAgE;IAChE,MAAM,KAAK,GAAG,0BAA0B,CAAC,OAAO,EAAE,KAAK,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAC9E,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACpC,IAAI,CAAC;QACH,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,SAAS,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QACrF,MAAM,OAAO,GAAI,IAA+B,EAAE,EAAE,CAAC;QACrD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;QAChF,CAAC;QACD,MAAM,kBAAkB,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;QAC1D,GAAG,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QACjF,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;IAC5D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,KAAK,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QACvE,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mail-reply.d.ts","sourceRoot":"","sources":["../../src/tools/mail-reply.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"mail-reply.d.ts","sourceRoot":"","sources":["../../src/tools/mail-reply.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAKhE,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,EAAE,EAAE,MAAM,EAAE,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAmBD;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE,SAAS,GACd,OAAO,CAAC,WAAW,CAAC,CA8CtB"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { postGraph, patchGraph } from "../lib/graph-client.js";
|
|
2
|
-
import {
|
|
3
|
-
import { uploadAttachmentSession } from "../lib/upload-session.js";
|
|
2
|
+
import { attachFilesToDraft, resolveAttachmentsOrRefuse } from "../lib/attach.js";
|
|
4
3
|
import { log } from "../lib/log.js";
|
|
5
4
|
function addrs(list) {
|
|
6
5
|
return (list ?? []).map((r) => r.emailAddress?.address).filter((a) => !!a);
|
|
@@ -20,7 +19,7 @@ function recip(list) {
|
|
|
20
19
|
*/
|
|
21
20
|
export async function runMailReply(config, args) {
|
|
22
21
|
const replyAll = args.replyAll ?? false;
|
|
23
|
-
const files =
|
|
22
|
+
const files = resolveAttachmentsOrRefuse(config.accountId, args.attachments, "mail-reply");
|
|
24
23
|
const action = replyAll ? "createReplyAll" : "createReply";
|
|
25
24
|
const { json } = await postGraph(config, `/me/messages/${encodeURIComponent(args.messageId)}/${action}`, { comment: args.body }, { tool: "mail-reply" });
|
|
26
25
|
const draft = json;
|
|
@@ -38,23 +37,7 @@ export async function runMailReply(config, args) {
|
|
|
38
37
|
patch.bccRecipients = recip(bcc);
|
|
39
38
|
await patchGraph(config, `/me/messages/${encodeURIComponent(draft.id)}`, patch, { tool: "mail-reply" });
|
|
40
39
|
}
|
|
41
|
-
|
|
42
|
-
let chunks = 0;
|
|
43
|
-
if (f.mode === "session") {
|
|
44
|
-
chunks = await uploadAttachmentSession(config, draft.id, f);
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
await postGraph(config, `/me/messages/${encodeURIComponent(draft.id)}/attachments`, toInlineAttachment(f), { tool: "mail-reply" });
|
|
48
|
-
}
|
|
49
|
-
log({
|
|
50
|
-
event: "mail-reply",
|
|
51
|
-
op: "attach",
|
|
52
|
-
id: f.name,
|
|
53
|
-
bytes: f.size,
|
|
54
|
-
path: f.mode,
|
|
55
|
-
chunks,
|
|
56
|
-
});
|
|
57
|
-
}
|
|
40
|
+
await attachFilesToDraft(config, draft.id, files, "mail-reply");
|
|
58
41
|
await postGraph(config, `/me/messages/${encodeURIComponent(draft.id)}/send`, undefined, { tool: "mail-reply" });
|
|
59
42
|
log({
|
|
60
43
|
event: "mail-reply",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mail-reply.js","sourceRoot":"","sources":["../../src/tools/mail-reply.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"mail-reply.js","sourceRoot":"","sources":["../../src/tools/mail-reply.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,11 +4,35 @@ export interface MailSendResult {
|
|
|
4
4
|
status: "sent";
|
|
5
5
|
httpStatus: number;
|
|
6
6
|
messageId: string | null;
|
|
7
|
+
attachments: string[];
|
|
7
8
|
}
|
|
8
9
|
/**
|
|
9
|
-
* Send a message via
|
|
10
|
-
*
|
|
11
|
-
*
|
|
10
|
+
* Send a message via Microsoft Graph.
|
|
11
|
+
*
|
|
12
|
+
* Two transports, chosen by whether the message carries a file:
|
|
13
|
+
*
|
|
14
|
+
* - no attachments → `POST /me/sendMail`. One call, atomic.
|
|
15
|
+
* - attachments → `POST /me/messages` (draft) → per-file attach →
|
|
16
|
+
* `POST /me/messages/{id}/send`, reusing mail-reply's
|
|
17
|
+
* proven order.
|
|
18
|
+
*
|
|
19
|
+
* The switch is forced, not stylistic: `/me/sendMail` carries attachments inline
|
|
20
|
+
* in the request body and is bounded well below the plugin's 25 MB per-file cap,
|
|
21
|
+
* so a large file can only reach the wire through a draft's upload session.
|
|
22
|
+
* The `op: "transport"` line names the route actually taken — a `route=sendMail`
|
|
23
|
+
* line with `attachments>0` is the mis-wire signature and must never appear.
|
|
24
|
+
*
|
|
25
|
+
* `messageId` is null on BOTH routes. `/me/sendMail` returns 202 with no body,
|
|
26
|
+
* and on the draft route the draft id is consumed by `/send` — returning it
|
|
27
|
+
* would hand back a handle that 404s on the next call (Task 1688). Success is
|
|
28
|
+
* asserted on the Graph status, which `writeGraph` already enforces by throwing
|
|
29
|
+
* on any non-2xx; the literal status is logged rather than an assumed 202.
|
|
30
|
+
*
|
|
31
|
+
* The draft route is NOT atomic: an attach or send failing Graph-side leaves a
|
|
32
|
+
* draft in the Drafts folder, the same exposure mail-reply carries. It is
|
|
33
|
+
* operator-visible there, and greppable as an `op:"transport" route=draft-send`
|
|
34
|
+
* line with no terminal `sent` line. Paths validate before any Graph write, so
|
|
35
|
+
* the common failure — a bad path — still orphans nothing.
|
|
12
36
|
*/
|
|
13
37
|
export declare function runMailSend(config: GraphClientConfig, input: MessageInput): Promise<MailSendResult>;
|
|
14
38
|
//# sourceMappingURL=mail-send.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mail-send.d.ts","sourceRoot":"","sources":["../../src/tools/mail-send.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"mail-send.d.ts","sourceRoot":"","sources":["../../src/tools/mail-send.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAIpE,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,iBAAiB,EACzB,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,cAAc,CAAC,CAmDzB"}
|
|
@@ -1,25 +1,70 @@
|
|
|
1
1
|
import { postGraph } from "../lib/graph-client.js";
|
|
2
2
|
import { buildMessage } from "../lib/message.js";
|
|
3
|
+
import { attachFilesToDraft, resolveAttachmentsOrRefuse } from "../lib/attach.js";
|
|
3
4
|
import { log } from "../lib/log.js";
|
|
4
5
|
/**
|
|
5
|
-
* Send a message via
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Send a message via Microsoft Graph.
|
|
7
|
+
*
|
|
8
|
+
* Two transports, chosen by whether the message carries a file:
|
|
9
|
+
*
|
|
10
|
+
* - no attachments → `POST /me/sendMail`. One call, atomic.
|
|
11
|
+
* - attachments → `POST /me/messages` (draft) → per-file attach →
|
|
12
|
+
* `POST /me/messages/{id}/send`, reusing mail-reply's
|
|
13
|
+
* proven order.
|
|
14
|
+
*
|
|
15
|
+
* The switch is forced, not stylistic: `/me/sendMail` carries attachments inline
|
|
16
|
+
* in the request body and is bounded well below the plugin's 25 MB per-file cap,
|
|
17
|
+
* so a large file can only reach the wire through a draft's upload session.
|
|
18
|
+
* The `op: "transport"` line names the route actually taken — a `route=sendMail`
|
|
19
|
+
* line with `attachments>0` is the mis-wire signature and must never appear.
|
|
20
|
+
*
|
|
21
|
+
* `messageId` is null on BOTH routes. `/me/sendMail` returns 202 with no body,
|
|
22
|
+
* and on the draft route the draft id is consumed by `/send` — returning it
|
|
23
|
+
* would hand back a handle that 404s on the next call (Task 1688). Success is
|
|
24
|
+
* asserted on the Graph status, which `writeGraph` already enforces by throwing
|
|
25
|
+
* on any non-2xx; the literal status is logged rather than an assumed 202.
|
|
26
|
+
*
|
|
27
|
+
* The draft route is NOT atomic: an attach or send failing Graph-side leaves a
|
|
28
|
+
* draft in the Drafts folder, the same exposure mail-reply carries. It is
|
|
29
|
+
* operator-visible there, and greppable as an `op:"transport" route=draft-send`
|
|
30
|
+
* line with no terminal `sent` line. Paths validate before any Graph write, so
|
|
31
|
+
* the common failure — a bad path — still orphans nothing.
|
|
8
32
|
*/
|
|
9
33
|
export async function runMailSend(config, input) {
|
|
10
34
|
const account = config.accountId;
|
|
11
35
|
log({ event: "send-request", account, to: input.to.length, subjLen: input.subject.length });
|
|
36
|
+
// Before any Graph write: a bad path must leave nothing behind.
|
|
37
|
+
const files = resolveAttachmentsOrRefuse(account, input.attachments, "mail-send");
|
|
12
38
|
const message = buildMessage(input);
|
|
13
39
|
const recipients = input.to.length + (input.cc?.length ?? 0) + (input.bcc?.length ?? 0);
|
|
14
40
|
log({ event: "send-args", account, recipients, hasBody: input.body.length > 0 });
|
|
41
|
+
const route = files.length > 0 ? "draft-send" : "sendMail";
|
|
42
|
+
log({ event: "mail-send", op: "transport", route, attachments: files.length });
|
|
15
43
|
try {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
44
|
+
if (files.length === 0) {
|
|
45
|
+
const { status, json } = await postGraph(config, "/me/sendMail", { message, saveToSentItems: true }, { tool: "mail-send" });
|
|
46
|
+
const messageId = json?.id ?? null;
|
|
47
|
+
log({ event: "sent", account, status, messageId, route });
|
|
48
|
+
return { status: "sent", httpStatus: status, messageId, attachments: [] };
|
|
49
|
+
}
|
|
50
|
+
const { json } = await postGraph(config, "/me/messages", message, { tool: "mail-send" });
|
|
51
|
+
const draftId = json?.id;
|
|
52
|
+
if (!draftId) {
|
|
53
|
+
throw new Error("outlook-mail-send: Graph created the draft but returned no id.");
|
|
54
|
+
}
|
|
55
|
+
// The orphan diagnostic, mirroring draft.ts's `draft-created` / draft-send.ts's
|
|
56
|
+
// `draft-sent` pairing. Emitted the moment a draft exists, because from here on
|
|
57
|
+
// a failure strands it: this id is the only handle the operator has to find it.
|
|
58
|
+
// Its absence also proves the CREATE itself failed, which strands nothing —
|
|
59
|
+
// without it, those two outcomes are indistinguishable in the log.
|
|
60
|
+
log({ event: "mail-send", op: "draft-created", account, id: draftId, route });
|
|
61
|
+
await attachFilesToDraft(config, draftId, files, "mail-send");
|
|
62
|
+
const { status } = await postGraph(config, `/me/messages/${encodeURIComponent(draftId)}/send`, undefined, { tool: "mail-send" });
|
|
63
|
+
log({ event: "sent", account, status, messageId: null, route });
|
|
64
|
+
return { status: "sent", httpStatus: status, messageId: null, attachments: files.map((f) => f.name) };
|
|
20
65
|
}
|
|
21
66
|
catch (err) {
|
|
22
|
-
log({ event: "send-failed", account, error: err.message });
|
|
67
|
+
log({ event: "send-failed", account, route, error: err.message });
|
|
23
68
|
throw err;
|
|
24
69
|
}
|
|
25
70
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mail-send.js","sourceRoot":"","sources":["../../src/tools/mail-send.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAqB,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"mail-send.js","sourceRoot":"","sources":["../../src/tools/mail-send.ts"],"names":[],"mappings":"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"}
|
|
@@ -132,13 +132,21 @@ Recoverable — moves to Deleted Items, never hard-deletes or expunges. An id al
|
|
|
132
132
|
|
|
133
133
|
## outlook-draft-edit
|
|
134
134
|
|
|
135
|
-
**
|
|
135
|
+
**Endpoints (in order):**
|
|
136
|
+
1. `PATCH /me/messages/{draftId}` (`ResponseType.RAW`; success asserted on HTTP 200) with only the supplied fields (`subject`, `body` → `{ contentType, content }`, `toRecipients`, `ccRecipients`, `bccRecipients`). Graph drafts are mutable, so no delete-and-reappend. **Skipped entirely** when the call supplies attachments and no patchable field. A PATCH on a sent (non-draft) message returns a Graph error surfaced unchanged.
|
|
137
|
+
2. Per file, by size — identical to `outlook-mail-reply` step 3 (shared helper). Attachments are **additive**: files are added to whatever the draft already carries. Removing or replacing an existing attachment is not supported.
|
|
138
|
+
|
|
139
|
+
At least one patchable field **or** one attachment is required. Attachment paths are validated before step 1, so a bad path leaves the draft untouched rather than half-edited.
|
|
140
|
+
|
|
141
|
+
Step 2 targets the id step 1 **reported**, not the caller's `draftId`. Ids are immutable plugin-wide so the two normally match, but a rotated id means the caller's handle no longer addresses the draft — see [Item id contract](#item-id-contract).
|
|
136
142
|
|
|
137
|
-
**Returned shape:** `{ draftId: string; updated: true }` — `draftId` is the id Graph reported for the updated draft, which is authoritative over the id passed in. The tool refuses rather than report `updated: true` for an id it has not read back.
|
|
143
|
+
**Returned shape:** `{ draftId: string; updated: true; attachments: string[] }` — `draftId` is the id Graph reported for the updated draft, which is authoritative over the id passed in. The tool refuses rather than report `updated: true` for an id it has not read back. An attachments-only edit issues no PATCH, so it reports the caller's id.
|
|
138
144
|
|
|
139
145
|
## outlook-mail-send
|
|
140
146
|
|
|
141
|
-
|
|
147
|
+
Two transports, chosen by whether the message carries a file. The `op: "transport"` log line names the route actually taken.
|
|
148
|
+
|
|
149
|
+
**Route A — no attachments. Endpoint:** `POST /me/sendMail`
|
|
142
150
|
|
|
143
151
|
**Request body:**
|
|
144
152
|
```json
|
|
@@ -156,13 +164,28 @@ Recoverable — moves to Deleted Items, never hard-deletes or expunges. An id al
|
|
|
156
164
|
|
|
157
165
|
**Success:** HTTP `202 Accepted`, empty body. No message id is returned. The tool asserts on the 202 — an empty body is not proof of send.
|
|
158
166
|
|
|
167
|
+
**Route B — with attachments. Endpoints (in order):**
|
|
168
|
+
1. `POST /me/messages` with the `message` object above (no `saveToSentItems` wrapper) → 201 with the created draft; `id` is used for steps 2-3 only.
|
|
169
|
+
2. Per file, by size — identical to `outlook-mail-reply` step 3 (shared helper).
|
|
170
|
+
3. `POST /me/messages/{draftId}/send` → 202. Graph consumes the draft and saves to Sent Items.
|
|
171
|
+
|
|
172
|
+
The switch is forced, not stylistic: `/me/sendMail` carries attachments inline in the request body and is bounded well below the plugin's 25 MB per-file cap, so a large file can only reach the wire through a draft's upload session. A `route=sendMail` line with `attachments>0` is the mis-wire signature and must never appear.
|
|
173
|
+
|
|
174
|
+
Route B is **not atomic**, unlike route A: an attach or send failing Graph-side leaves a draft in the Drafts folder — the same exposure `outlook-mail-reply` carries, and not cleaned up here either. Attachment paths are validated before step 1, so the common failure (a bad path) leaves nothing behind.
|
|
175
|
+
|
|
176
|
+
**Finding an orphan.** Step 1 emits `op:"draft-created" id=<draftId>` the moment a draft exists, mirroring `outlook-draft`'s `draft-created` / `outlook-draft-send`'s `draft-sent` pairing. The orphan signature is a `draft-created` line with **no** terminal `sent` line, and the `id` on it is the handle needed to go delete the draft. The `op:"transport"` line alone is not the signature: it is emitted before the create, so a transport line with no `draft-created` means the create itself failed and stranded nothing.
|
|
177
|
+
|
|
178
|
+
**Returned shape:** `{ status: "sent"; httpStatus: number; messageId: string | null; attachments: string[] }` — `messageId` is `null` on **both** routes. Route A returns no body; on route B the draft id is consumed by `/send` and would 404 if handed back — see [Item id contract](#item-id-contract).
|
|
179
|
+
|
|
159
180
|
## outlook-draft
|
|
160
181
|
|
|
161
|
-
**
|
|
182
|
+
**Endpoints (in order):**
|
|
183
|
+
1. `POST /me/messages` with the `message` object above (no `saveToSentItems` wrapper).
|
|
184
|
+
2. Per file, by size — identical to `outlook-mail-reply` step 3 (shared helper). Attachment paths are validated before step 1, so a bad path leaves no orphan draft.
|
|
162
185
|
|
|
163
|
-
**
|
|
186
|
+
**Success:** HTTP `201 Created` with the created message JSON; `id` is the handle passed to `outlook-draft-send`, and the returned draft already carries the files. It is durable only because the plugin opts into immutable ids — see [Item id contract](#item-id-contract).
|
|
164
187
|
|
|
165
|
-
**
|
|
188
|
+
**Returned shape:** `{ draftId: string; attachments: string[] }`.
|
|
166
189
|
|
|
167
190
|
## outlook-draft-send
|
|
168
191
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: outlook
|
|
3
|
-
description: "Microsoft 365 / Outlook.com via Microsoft Graph. Mail is read (list, search, full-body), reply, delete, and compose (draft, draft-edit, send); contacts are read-only; the calendar is read plus control. Use when the user asks about their Outlook mail, calendar, or contacts, wants to read a full message, reply, delete, send or draft a message, or asks to book, move, cancel, or accept a calendar event. First-time use requires outlook-account-register plus outlook-account-register-poll; subsequent use is transparent. Tools: outlook-mail-list / outlook-mail-search (inbox triage, ~255-char preview, date window + nextCursor paging), outlook-mail-fetch-body (full body of one message), outlook-mail-reply (in-thread reply with attachments), outlook-mail-delete (move to Deleted Items), outlook-mail-otp-extract (poll for a one-time code), outlook-mail-send / outlook-draft / outlook-draft-edit / outlook-draft-send (compose, edit, send), outlook-calendar-list / outlook-calendar-event (read calendar), outlook-calendar-create / outlook-calendar-update / outlook-calendar-cancel / outlook-calendar-respond / outlook-calendar-freebusy (control calendar), outlook-contacts-list (contacts), outlook-mailbox-info (health), outlook-account-register / outlook-account-register-poll (device-code register)."
|
|
3
|
+
description: "Microsoft 365 / Outlook.com via Microsoft Graph. Mail is read (list, search, full-body), reply, delete, and compose (draft, draft-edit, send); contacts are read-only; the calendar is read plus control. Use when the user asks about their Outlook mail, calendar, or contacts, wants to read a full message, reply, delete, send or draft a message, or asks to book, move, cancel, or accept a calendar event. First-time use requires outlook-account-register plus outlook-account-register-poll; subsequent use is transparent. Tools: outlook-mail-list / outlook-mail-search (inbox triage, ~255-char preview, date window + nextCursor paging), outlook-mail-fetch-body (full body of one message), outlook-mail-reply (in-thread reply with attachments), outlook-mail-delete (move to Deleted Items), outlook-mail-otp-extract (poll for a one-time code), outlook-mail-send / outlook-draft / outlook-draft-edit / outlook-draft-send (compose, edit, send, with attachments), outlook-calendar-list / outlook-calendar-event (read calendar), outlook-calendar-create / outlook-calendar-update / outlook-calendar-cancel / outlook-calendar-respond / outlook-calendar-freebusy (control calendar), outlook-contacts-list (contacts), outlook-mailbox-info (health), outlook-account-register / outlook-account-register-poll (device-code register)."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Outlook (Microsoft Graph)
|
|
@@ -50,7 +50,7 @@ The revoke path below is scoped to one specific, visible failure. It is not a ge
|
|
|
50
50
|
## Reading mail
|
|
51
51
|
|
|
52
52
|
- `outlook-mail-list` — mail newest-first, default top=25, folder=Inbox. Returns Microsoft's short `bodyPreview` (~255 chars) for cheap triage. Add `since`/`before` (ISO date-times) for a date window. To reach mail older than the newest page, pass the response's `nextCursor` back as `cursor` — repeat until `nextCursor` is null. Filter by sender or subject via `outlook-mail-search`.
|
|
53
|
-
- `outlook-mail-search query=<keyword>` — Microsoft Graph `$search` (KQL) over the mailbox; same preview-only shape as the list.
|
|
53
|
+
- `outlook-mail-search query=<keyword>` — Microsoft Graph `$search` (KQL) over the mailbox; same preview-only shape as the list. Filters go **inside the `query` string** as KQL text, not as tool parameters: `query="from:sam@example.com"`, `query="subject:invoice received>=2024-01-01"`, or free text. The bare `from:`/`to:` form is KQL syntax that lives inside that one string; it is not how recipients are written anywhere else in this plugin (see Sending and drafting, where `to` is a JSON array of quoted strings). Page with `nextCursor` → `cursor` like the list. (Graph forbids `$search`+date-`$filter`, so a strict date window is on `outlook-mail-list`.)
|
|
54
54
|
- `outlook-mail-fetch-body messageId=<id>` — the COMPLETE body of one message (HTML decoded to text, no preview cap). This is the full-read path: `outlook-mail-list` / `outlook-mail-search` return previews only, so when you must read or act on a message's actual content, fetch the body by its id. Also returns the envelope (from, to, cc, subject, date, conversationId).
|
|
55
55
|
- `outlook-mail-attachment messageId=<id>` — LIST a message's attachments (`id`, `name`, `contentType`, `size`, `isInline`, `kind`). `outlook-mail-list` / `outlook-mail-search` / `outlook-mail-fetch-body` all report `hasAttachments`; when it is true, call this to see what is attached.
|
|
56
56
|
- `outlook-mail-attachment messageId=<id> attachmentId=<id>` — DOWNLOAD one file attachment. The bytes are saved under the account's `uploads/outlook` folder and the absolute path is returned, ready for the Read tool, `SendUserFile`, or an `outlook-mail-reply` attachment. Only file attachments download: `item` and `reference` kinds are refused, as are empty payloads and anything over 25 MB. Needs `Mail.Read`.
|
|
@@ -66,9 +66,26 @@ The revoke path below is scoped to one specific, visible failure. It is not a ge
|
|
|
66
66
|
|
|
67
67
|
## Sending and drafting
|
|
68
68
|
|
|
69
|
-
- `outlook-mail-send` — send a message. `to` is an array of addresses (at least one); `cc`/`bcc` optional arrays; `subject`; `body`; `isHtml` true for an HTML body, otherwise plain text. A copy is saved to Sent Items. Success is confirmed on Graph's 202 response.
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
- `outlook-mail-send` — send a message. `to` is an array of addresses (at least one); `cc`/`bcc` optional arrays; `subject`; `body`; `isHtml` true for an HTML body, otherwise plain text; `attachments` an array of file paths inside the account directory, up to 25 MB each. A copy is saved to Sent Items. Success is confirmed on Graph's 202 response.
|
|
70
|
+
|
|
71
|
+
Every address is a **quoted string inside an array**, even when there is only one recipient, and `mailbox` is a plain quoted string. `attachments` takes the same shape: an array of quoted absolute paths.
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"mailbox": "rob@example.com",
|
|
76
|
+
"to": ["rob@example.com"],
|
|
77
|
+
"cc": ["tom@example.com"],
|
|
78
|
+
"subject": "Pricing schedule",
|
|
79
|
+
"body": "Schedule attached.",
|
|
80
|
+
"isHtml": false,
|
|
81
|
+
"attachments": ["/path/to/account/uploads/outlook/schedule.pdf"]
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`"to": rob@example.com` (unquoted) and `"to": "rob@example.com"` (quoted but not an array) are both rejected before the call reaches Outlook. A rejection here means the arguments were malformed; it is not evidence that sending is broken. Check `outlook-mailbox-info` before reporting a send fault.
|
|
86
|
+
|
|
87
|
+
- `outlook-draft` — create a draft in the Drafts folder (same fields, same array-of-quoted-strings shape as send, including `attachments`). Returns the draft's id, and the files are already on the draft, so it can be sent as-is.
|
|
88
|
+
- `outlook-draft-edit draftId=<id>` — update an existing draft in place (Graph drafts are mutable, so no delete-and-reappend). Pass the draftId plus only the fields to change: `to`, `cc`, `bcc`, `subject`, `body`, `isHtml`, `attachments` (recipients and attachments take the same array-of-quoted-strings shape as send). Attachments are added to the draft, never removed. At least one field or attachment is required; a sent message cannot be edited.
|
|
72
89
|
- `outlook-draft-send draftId=<id>` — send a draft created earlier. Graph consumes the draft on send.
|
|
73
90
|
|
|
74
91
|
If the mailbox was registered before send was available, the first send returns "re-register to grant send" — run `outlook-account-register` again and consent to the send scopes.
|
|
@@ -17,6 +17,18 @@ tools:
|
|
|
17
17
|
- name: storage-r2-bucket-create
|
|
18
18
|
publicAllowlist: false
|
|
19
19
|
adminAllowlist: true
|
|
20
|
+
- name: storage-r2-object-list
|
|
21
|
+
publicAllowlist: false
|
|
22
|
+
adminAllowlist: true
|
|
23
|
+
- name: storage-r2-object-get
|
|
24
|
+
publicAllowlist: false
|
|
25
|
+
adminAllowlist: true
|
|
26
|
+
- name: storage-r2-object-put
|
|
27
|
+
publicAllowlist: false
|
|
28
|
+
adminAllowlist: true
|
|
29
|
+
- name: storage-r2-object-delete
|
|
30
|
+
publicAllowlist: false
|
|
31
|
+
adminAllowlist: true
|
|
20
32
|
metadata: {"platform":{"embed":["admin"]}}
|
|
21
33
|
mcp:
|
|
22
34
|
command: node
|
|
@@ -35,7 +47,7 @@ mcp-manifest: auto
|
|
|
35
47
|
|
|
36
48
|
# storage-broker
|
|
37
49
|
|
|
38
|
-
The isolation boundary for per-account Cloudflare D1 and R2 on a multi-tenant install. A sub-account agent is issued no Cloudflare credential: its `cloudflare.env` carries no account-wide token, and `cf-token.sh` refuses to mint one for a non-house caller. The broker is therefore the only *sanctioned* route to D1 and R2, and it denies cross-account access before calling Cloudflare. It is not an OS-enforced boundary: all accounts share one unix user, so an agent holding `Bash` can read the house credential and reach `wrangler` outside the broker. Closing that requires OS user separation,
|
|
50
|
+
The isolation boundary for per-account Cloudflare D1 and R2 on a multi-tenant install. A sub-account agent is issued no Cloudflare credential: its `cloudflare.env` carries no account-wide token, and `cf-token.sh` refuses to mint one for a non-house caller. The broker is therefore the only *sanctioned* route to D1 and R2, and it denies cross-account access before calling Cloudflare. It is not an OS-enforced boundary: all accounts share one unix user, so an agent holding `Bash` can read the house credential and reach `wrangler` outside the broker. Closing that requires OS user separation, which was costed and declined as disproportionate — so this stays a sanctioned-path boundary permanently, and nothing tracks closing it. Every D1 and R2 operation goes through these tools, which forward the caller's account as the platform-stamped `x-maxy-caller-account` header (never a tool argument) to the house-level broker service. The service holds the sole account-wide credential in `config/cloudflare-house.env`, resolves each resource's recorded owner, and denies any cross-account access before it ever calls Cloudflare.
|
|
39
51
|
|
|
40
52
|
## Tools
|
|
41
53
|
|
|
@@ -44,9 +56,36 @@ The isolation boundary for per-account Cloudflare D1 and R2 on a multi-tenant in
|
|
|
44
56
|
- `storage-d1-query` — run SQL against a database the caller owns.
|
|
45
57
|
- `storage-r2-bucket-list` — the caller account's R2 buckets.
|
|
46
58
|
- `storage-r2-bucket-create` — create an R2 bucket, registered to the caller.
|
|
59
|
+
- `storage-r2-object-list` — the objects in a bucket the caller owns.
|
|
60
|
+
- `storage-r2-object-get` — download an object to a file in the caller's account directory. Objects over 100 MiB are rejected.
|
|
61
|
+
- `storage-r2-object-put` — upload a file from the caller's account directory. Files over 100 MiB are rejected.
|
|
62
|
+
- `storage-r2-object-delete` — delete an object from a bucket the caller owns.
|
|
47
63
|
|
|
48
64
|
A request against a database or bucket the caller does not own returns an authorization error, not another account's data.
|
|
49
65
|
|
|
66
|
+
## Where the object tools do their file I/O, and why
|
|
67
|
+
|
|
68
|
+
`storage-r2-object-get` writes to a file and `storage-r2-object-put` reads from one, rather than passing bytes through the tool result, because these objects are photos and scans and base64 in a result would push megabytes into the agent's context window.
|
|
69
|
+
|
|
70
|
+
That file I/O happens in this MCP process, under the caller's own account, and never in the house-level broker service. The service holds the account-wide credential, so a caller-supplied path resolved there would be a house-privileged arbitrary read and write reachable from any sub-account — a `put` sourced from `config/cloudflare-house.env` would copy the house master into the caller's own bucket, to be read back legitimately afterwards. Keeping the service free of any filesystem access removes that class entirely, and it needs no `Bash`, so OS user separation would not have closed it.
|
|
71
|
+
|
|
72
|
+
The paths are confined to the caller's account directory by realpath, which a symlink cannot escape. That is a correctness guardrail rather than a boundary: all accounts share one unix user, so an agent holding `Bash` can already read whatever this process can.
|
|
73
|
+
|
|
74
|
+
## The 100 MiB object cap
|
|
75
|
+
|
|
76
|
+
`storage-r2-object-get` and `storage-r2-object-put` refuse any object over 100 MiB. The number lives once, in `platform/lib/storage-broker/src/object-limits.ts`, and both the tools and the broker routes read it from there.
|
|
77
|
+
|
|
78
|
+
It is set independently of the email plugin's 25 MiB attachment cap. Both bound the same class of payload, but for different reasons, and so they move for different reasons: email's tracks an SMTP-side rejection threshold, this one tracks memory in the house process. R2 does not bind either — a single-part upload may be up to 5 GiB — so this cap is the only bound on either transfer.
|
|
79
|
+
|
|
80
|
+
Both directions are checked before anything is buffered, in two places that are not redundant:
|
|
81
|
+
|
|
82
|
+
- `put` is size-checked in this MCP process with `statSync` before the file is read. That is what produces the error naming the file and the limit.
|
|
83
|
+
- The house routes enforce independently, because they are plain loopback HTTP and every agent holds `Bash`: a cap enforced only here is bypassed by one `curl`, and the house process is the one holding the account-wide credential. `/r2/object/put` rejects an over-cap body at Hono's `bodyLimit`, before the body is buffered. `get` is sized from `r2ObjectList`'s recorded `size` before any bytes move.
|
|
84
|
+
|
|
85
|
+
A `get` whose key has no recorded size in the listing is refused, not attempted. No recorded size means no bound, and a stale or lagging listing would otherwise transfer an unbounded object. The cost is that a genuinely missing object reports "no recorded size" rather than Cloudflare's 404.
|
|
86
|
+
|
|
87
|
+
The cap bounds the spike; it does not make it small. At 100 MiB a `get` still leaves the house process holding the object and its base64 form, roughly 2.4x. Streaming and multipart transfer are the real fix for large objects and are deliberately not implemented; the cap stands in for them.
|
|
88
|
+
|
|
50
89
|
## Wrangler resolution
|
|
51
90
|
|
|
52
|
-
The broker service shells wrangler through `npx wrangler`, never bare `wrangler`, for every D1 operation and R2 bucket creation. The brand server PATH carries no global wrangler install; only `npx wrangler` resolves, so a bare `wrangler` call ENOENTs those operations on a real install. R2 bucket listing
|
|
91
|
+
The broker service shells wrangler through `npx wrangler`, never bare `wrangler`, for every D1 operation and R2 bucket creation. The brand server PATH carries no global wrangler install; only `npx wrangler` resolves, so a bare `wrangler` call ENOENTs those operations on a real install. R2 bucket listing and every R2 object operation are exempt: they read the Cloudflare REST API directly, not wrangler.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confine-path.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/confine-path.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll } from "vitest";
|
|
2
|
+
import { mkdtempSync, mkdirSync, writeFileSync, symlinkSync, rmSync, realpathSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { confineToAccountDir } from "../confine-path.js";
|
|
6
|
+
let root;
|
|
7
|
+
let accountDir;
|
|
8
|
+
let outside;
|
|
9
|
+
beforeAll(() => {
|
|
10
|
+
// realpath the tmp root: on macOS /var is a symlink to /private/var, so an
|
|
11
|
+
// un-resolved base would never prefix-match the resolved candidates.
|
|
12
|
+
root = realpathSync(mkdtempSync(join(tmpdir(), "confine-")));
|
|
13
|
+
accountDir = join(root, "accounts", "acc-a");
|
|
14
|
+
outside = join(root, "elsewhere");
|
|
15
|
+
mkdirSync(accountDir, { recursive: true });
|
|
16
|
+
mkdirSync(outside, { recursive: true });
|
|
17
|
+
writeFileSync(join(accountDir, "ok.txt"), "hi");
|
|
18
|
+
writeFileSync(join(outside, "secret.env"), "TOKEN=x");
|
|
19
|
+
mkdirSync(join(accountDir, "sub"), { recursive: true });
|
|
20
|
+
// The escape a plain string prefix check would miss.
|
|
21
|
+
symlinkSync(join(outside, "secret.env"), join(accountDir, "escape.txt"));
|
|
22
|
+
// A symlinked *directory* inside the account dir, pointing out.
|
|
23
|
+
symlinkSync(outside, join(accountDir, "outlink"));
|
|
24
|
+
process.env.ACCOUNT_DIR = accountDir;
|
|
25
|
+
});
|
|
26
|
+
afterAll(() => {
|
|
27
|
+
delete process.env.ACCOUNT_DIR;
|
|
28
|
+
rmSync(root, { recursive: true, force: true });
|
|
29
|
+
});
|
|
30
|
+
describe("confineToAccountDir", () => {
|
|
31
|
+
it("accepts a regular file inside the account dir", () => {
|
|
32
|
+
expect(confineToAccountDir(join(accountDir, "ok.txt"), true)).toBe(join(accountDir, "ok.txt"));
|
|
33
|
+
});
|
|
34
|
+
it("rejects a .. traversal out of the account dir", () => {
|
|
35
|
+
expect(() => confineToAccountDir(join(accountDir, "..", "..", "elsewhere", "secret.env"), true)).toThrow(/outside the account directory/);
|
|
36
|
+
});
|
|
37
|
+
it("rejects a symlink resolving outside the account dir", () => {
|
|
38
|
+
expect(() => confineToAccountDir(join(accountDir, "escape.txt"), true)).toThrow(/outside the account directory/);
|
|
39
|
+
});
|
|
40
|
+
it("rejects a directory passed where a file is required", () => {
|
|
41
|
+
expect(() => confineToAccountDir(join(accountDir, "sub"), true)).toThrow(/not a regular file/);
|
|
42
|
+
});
|
|
43
|
+
it("reports a missing source file as not found, not as an escape", () => {
|
|
44
|
+
expect(() => confineToAccountDir(join(accountDir, "nope.txt"), true)).toThrow(/File not found/);
|
|
45
|
+
});
|
|
46
|
+
it("rejects a not-yet-existing destination whose parent is outside", () => {
|
|
47
|
+
expect(() => confineToAccountDir(join(outside, "new.bin"), false)).toThrow(/outside the account directory/);
|
|
48
|
+
});
|
|
49
|
+
// The destination branch cannot realpath a path that does not exist, so it
|
|
50
|
+
// resolves the parent. When the final component *does* already exist and is a
|
|
51
|
+
// symlink out, resolving only the parent would pass it and the caller's write
|
|
52
|
+
// would follow the link out of the account directory — the same escape the
|
|
53
|
+
// source branch rejects.
|
|
54
|
+
it("rejects a destination whose existing final component is a symlink out", () => {
|
|
55
|
+
expect(() => confineToAccountDir(join(accountDir, "escape.txt"), false)).toThrow(/outside the account directory/);
|
|
56
|
+
});
|
|
57
|
+
it("rejects a destination reached through a symlinked parent directory", () => {
|
|
58
|
+
expect(() => confineToAccountDir(join(accountDir, "outlink", "new.bin"), false)).toThrow(/outside the account directory/);
|
|
59
|
+
});
|
|
60
|
+
it("accepts a not-yet-existing destination inside the account dir", () => {
|
|
61
|
+
expect(confineToAccountDir(join(accountDir, "new.bin"), false)).toBe(join(accountDir, "new.bin"));
|
|
62
|
+
});
|
|
63
|
+
it("accepts a not-yet-existing destination in a subdirectory", () => {
|
|
64
|
+
expect(confineToAccountDir(join(accountDir, "sub", "new.bin"), false)).toBe(join(accountDir, "sub", "new.bin"));
|
|
65
|
+
});
|
|
66
|
+
it("throws when no account context is resolvable, rather than defaulting open", () => {
|
|
67
|
+
const saved = process.env.ACCOUNT_DIR;
|
|
68
|
+
const savedRoot = process.env.PLATFORM_ROOT;
|
|
69
|
+
const savedId = process.env.ACCOUNT_ID;
|
|
70
|
+
delete process.env.ACCOUNT_DIR;
|
|
71
|
+
delete process.env.PLATFORM_ROOT;
|
|
72
|
+
delete process.env.ACCOUNT_ID;
|
|
73
|
+
try {
|
|
74
|
+
expect(() => confineToAccountDir(join(accountDir, "ok.txt"), true)).toThrow(/No account context/);
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
process.env.ACCOUNT_DIR = saved;
|
|
78
|
+
if (savedRoot !== undefined)
|
|
79
|
+
process.env.PLATFORM_ROOT = savedRoot;
|
|
80
|
+
if (savedId !== undefined)
|
|
81
|
+
process.env.ACCOUNT_ID = savedId;
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
//# sourceMappingURL=confine-path.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confine-path.test.js","sourceRoot":"","sources":["../../src/__tests__/confine-path.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"put-source.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/put-source.test.ts"],"names":[],"mappings":""}
|