@rubytech/create-maxy-code 0.1.356 → 0.1.358
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/dist/__tests__/claude-ptys-slice.test.js +36 -1
- package/dist/port-resolution.js +18 -1
- package/package.json +1 -1
- package/payload/platform/package-lock.json +237 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/capabilities-here.test.js +79 -1
- package/payload/platform/plugins/admin/mcp/dist/__tests__/capabilities-here.test.js.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js +17 -3
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.d.ts +16 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.d.ts.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.js +52 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.js.map +1 -1
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +59 -1
- package/payload/platform/plugins/cloudflare/PLUGIN.md +7 -1
- package/payload/platform/plugins/cloudflare/bin/__tests__/cf-token.test.sh +195 -0
- package/payload/platform/plugins/cloudflare/bin/cf-token.sh +157 -0
- package/payload/platform/plugins/cloudflare/references/api.md +2 -0
- package/payload/platform/plugins/cloudflare/skills/cloudflare/SKILL.md +14 -0
- package/payload/platform/plugins/cloudflare/skills/site-deploy/SKILL.md +10 -1
- package/payload/platform/plugins/docs/PLUGIN.md +2 -0
- package/payload/platform/plugins/docs/references/claudeai-connectors.md +45 -0
- package/payload/platform/plugins/docs/references/cloudflare.md +6 -0
- package/payload/platform/plugins/docs/references/troubleshooting.md +2 -0
- package/payload/platform/plugins/email/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/email/PLUGIN.md +7 -3
- package/payload/platform/plugins/email/mcp/dist/__tests__/body-extract.test.d.ts +2 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/body-extract.test.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/body-extract.test.js +99 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/body-extract.test.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/compose-draft-id.test.d.ts +2 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/compose-draft-id.test.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/compose-draft-id.test.js +32 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/compose-draft-id.test.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/credentials.test.d.ts +2 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/credentials.test.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/credentials.test.js +109 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/credentials.test.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft-edit.test.js +64 -6
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft-edit.test.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft.test.js +12 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft.test.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-setup.test.js +22 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-setup.test.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/__tests__/imap-drafts.test.js +94 -9
- package/payload/platform/plugins/email/mcp/dist/__tests__/imap-drafts.test.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/__tests__/mail-options.test.js +4 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/mail-options.test.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/index.js +39 -2
- package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/compose.d.ts +5 -0
- package/payload/platform/plugins/email/mcp/dist/lib/compose.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/compose.js +8 -1
- package/payload/platform/plugins/email/mcp/dist/lib/compose.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/credentials.d.ts +14 -7
- package/payload/platform/plugins/email/mcp/dist/lib/credentials.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/credentials.js +87 -31
- package/payload/platform/plugins/email/mcp/dist/lib/credentials.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts +68 -33
- package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/imap.js +253 -97
- package/payload/platform/plugins/email/mcp/dist/lib/imap.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/smtp.d.ts +6 -0
- package/payload/platform/plugins/email/mcp/dist/lib/smtp.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/smtp.js +4 -0
- package/payload/platform/plugins/email/mcp/dist/lib/smtp.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-draft-edit.d.ts +8 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-draft-edit.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-draft-edit.js +10 -6
- package/payload/platform/plugins/email/mcp/dist/tools/email-draft-edit.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-draft.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-draft.js +4 -2
- package/payload/platform/plugins/email/mcp/dist/tools/email-draft.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-fetch-body.d.ts +15 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-fetch-body.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-fetch-body.js +29 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-fetch-body.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-setup.js +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-setup.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-status.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-status.js +2 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-status.js.map +1 -1
- package/payload/platform/plugins/email/mcp/package.json +2 -0
- package/payload/platform/plugins/email/references/email-reference.md +21 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-bootstrap.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-bootstrap.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-bootstrap.test.js +60 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-bootstrap.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js +11 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js.map +1 -1
- package/payload/platform/scripts/rss-sampler.sh +21 -0
- 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 +1 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/connector-auth.d.ts +31 -0
- package/payload/platform/services/claude-session-manager/dist/connector-auth.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/connector-auth.js +49 -0
- package/payload/platform/services/claude-session-manager/dist/connector-auth.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +24 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +214 -17
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +2 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +24 -4
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
- package/payload/platform/services/webchat-channel/dist/instructions.d.ts +1 -0
- package/payload/platform/services/webchat-channel/dist/instructions.d.ts.map +1 -1
- package/payload/platform/services/webchat-channel/dist/instructions.js +28 -0
- package/payload/platform/services/webchat-channel/dist/instructions.js.map +1 -1
- package/payload/platform/services/webchat-channel/dist/server.d.ts.map +1 -1
- package/payload/platform/services/webchat-channel/dist/server.js +75 -5
- package/payload/platform/services/webchat-channel/dist/server.js.map +1 -1
- package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
- package/payload/server/{chunk-25UVZNFA.js → chunk-CXEC5RWB.js} +26 -3
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/public/assets/{AdminLoginScreens-4I5CdIWl.js → AdminLoginScreens-Ro_RUTDh.js} +1 -1
- package/payload/server/public/assets/{AdminShell-Bc9F7X_N.js → AdminShell-CmqhpAPU.js} +1 -1
- package/payload/server/public/assets/{Checkbox-DoQV18Ow.js → Checkbox-dYLaMEdh.js} +1 -1
- package/payload/server/public/assets/OperatorConversations-2zJh7qnO.css +1 -0
- package/payload/server/public/assets/OperatorConversations-Cei12sSK.js +9 -0
- package/payload/server/public/assets/{admin-DLsMA09F.js → admin-BldZQrJF.js} +1 -1
- package/payload/server/public/assets/{arc-DpkSQNto.js → arc-Crkz45LN.js} +1 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-Dma4Bgha.js +1 -0
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-Bw7Yq8O9.js → architectureDiagram-Q4EWVU46-C9JThHgP.js} +1 -1
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-BBtJQFSz.js → blockDiagram-DXYQGD6D-DpsV_HXT.js} +1 -1
- package/payload/server/public/assets/{browser-w6rY_KAO.js → browser-DrXZ4wpy.js} +1 -1
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-JAMgxzOr.js → c4Diagram-AHTNJAMY-CTm_6aKi.js} +1 -1
- package/payload/server/public/assets/{calendar-CvEdDFYd.js → calendar-CQ4TCbO7.js} +1 -1
- package/payload/server/public/assets/channel-B_-5bau1.js +1 -0
- package/payload/server/public/assets/chat-D7w6_NE1.js +1 -0
- package/payload/server/public/assets/{chunk-2KRD3SAO-eDugsc2L.js → chunk-2KRD3SAO-1KSJW1q6.js} +1 -1
- package/payload/server/public/assets/{chunk-336JU56O-BXfECwdC.js → chunk-336JU56O-C3t08Ip-.js} +2 -2
- package/payload/server/public/assets/chunk-426QAEUC-Dirxdwjh.js +1 -0
- package/payload/server/public/assets/{chunk-4BX2VUAB-CByVeKCY.js → chunk-4BX2VUAB-kl1ze33n.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-CpY0DcUB.js → chunk-4TB4RGXK-DxpoDoHg.js} +1 -1
- package/payload/server/public/assets/{chunk-55IACEB6-C3YxgyIb.js → chunk-55IACEB6-WM9VJzha.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-Bc0d7j8A.js → chunk-5FUZZQ4R-CLH63ZH3.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-CTUGMRIa.js → chunk-5PVQY5BW-DCTb8IAT.js} +1 -1
- package/payload/server/public/assets/{chunk-67CJDMHE-CbdPnpLd.js → chunk-67CJDMHE-BSYzyaoH.js} +1 -1
- package/payload/server/public/assets/{chunk-7N4EOEYR-Cjl2kevz.js → chunk-7N4EOEYR-CarJ7O2P.js} +1 -1
- package/payload/server/public/assets/{chunk-AA7GKIK3-BgozIh5p.js → chunk-AA7GKIK3-DTNLyBpH.js} +1 -1
- package/payload/server/public/assets/{chunk-BSJP7CBP-6Ufq45NW.js → chunk-BSJP7CBP-BFmHflHY.js} +1 -1
- package/payload/server/public/assets/{chunk-CIAEETIT-BhhYuMnA.js → chunk-CIAEETIT-BpjG0IzU.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-BfaJ3yDD.js → chunk-EDXVE4YY-2fSowhlW.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-C7-hIGVC.js → chunk-ENJZ2VHE-ClcranGV.js} +1 -1
- package/payload/server/public/assets/{chunk-FMBD7UC4-BQvvyocF.js → chunk-FMBD7UC4-DLq4BItQ.js} +1 -1
- package/payload/server/public/assets/{chunk-FOC6F5B3-C6rBDOxH.js → chunk-FOC6F5B3-B9QI3Guz.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-BjOJvkVa.js → chunk-ICPOFSXX-BAr-Mv1Z.js} +2 -2
- package/payload/server/public/assets/{chunk-K5T4RW27-kKowRHhn.js → chunk-K5T4RW27-8PvAHasY.js} +1 -1
- package/payload/server/public/assets/{chunk-KGLVRYIC-bAEV_5eH.js → chunk-KGLVRYIC--aGaDRH5.js} +1 -1
- package/payload/server/public/assets/{chunk-LIHQZDEY-BLvsQAfg.js → chunk-LIHQZDEY-BrWj7mw2.js} +1 -1
- package/payload/server/public/assets/{chunk-ORNJ4GCN-BzYZfPZ7.js → chunk-ORNJ4GCN-kYQmkMTi.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-CL7WRw42.js → chunk-OYMX7WX6-CIsryCWH.js} +1 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-Duipo1kI.js +1 -0
- package/payload/server/public/assets/{chunk-U2HBQHQK-BdcVGjau.js → chunk-U2HBQHQK-68X653e3.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-C0CL_Jin.js → chunk-X2U36JSP-v3v9soX3.js} +1 -1
- package/payload/server/public/assets/{chunk-XPW4576I-CsV7CUxH.js → chunk-XPW4576I-CW5DPpop.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-Cj0_fLkR.js → chunk-YZCP3GAM-B1VP7Rl2.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-DLuquyoY.js → chunk-ZZ45TVLE-DiJrujNY.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-B3kX5SuB.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-D761c7Lr.js +1 -0
- package/payload/server/public/assets/clone-BrhFv6Tb.js +1 -0
- package/payload/server/public/assets/{cose-bilkent-S5V4N54A-BU3EOLXX.js → cose-bilkent-S5V4N54A-BscXxLMc.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-CoCAYhFc.js → dagre-KV5264BT-DVgGlTQ1.js} +1 -1
- package/payload/server/public/assets/{dagre-BRq8C9NU.js → dagre-MBv6WlCS.js} +1 -1
- package/payload/server/public/assets/data-Dc5XNWgF.js +1 -0
- package/payload/server/public/assets/{diagram-5BDNPKRD-DBy5GNaQ.js → diagram-5BDNPKRD-BmhCaO0a.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-l93MsBLv.js → diagram-G4DWMVQ6-ChU9L8vz.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-BIfXkVlL.js → diagram-MMDJMWI5-Oy409Zc_.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-BuTJLBny.js → diagram-TYMM5635-Cn1YByqk.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-BwPxWDbP.js → erDiagram-SMLLAGMA-e17yfLvr.js} +1 -1
- package/payload/server/public/assets/{flatten-DtM0XAMt.js → flatten-Cl1Bc3dR.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-BhPtHXMR.js → flowDiagram-DWJPFMVM-Dqt7sCVZ.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-BlXjprdD.js → ganttDiagram-T4ZO3ILL-B4IhwS2r.js} +1 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-BCQAqtLy.js +1 -0
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-BW36BDiu.js → gitGraphDiagram-UUTBAWPF-BDw-PKcI.js} +1 -1
- package/payload/server/public/assets/{graph-BrwfYOr3.js → graph-l_3_j3I9.js} +3 -3
- package/payload/server/public/assets/{graph-labels-BNZ8u8TE.js → graph-labels-CJj55nHk.js} +1 -1
- package/payload/server/public/assets/{graphlib-Bjg3Tq-k.js → graphlib-DPIYk_sg.js} +1 -1
- package/payload/server/public/assets/info-OMHHGYJF-qU7hK50T.js +1 -0
- package/payload/server/public/assets/infoDiagram-42DDH7IO-KGdcJeTs.js +2 -0
- package/payload/server/public/assets/{isEmpty-D__iH1WQ.js → isEmpty-D1pGOD1J.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-CmB-bA_O.js → ishikawaDiagram-UXIWVN3A-BRIHsB3G.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-Bt1fJKmc.js → journeyDiagram-VCZTEJTY-aKNgmoHd.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-DVbksz_J.js → kanban-definition-6JOO6SKY-cwBwT-nm.js} +1 -1
- package/payload/server/public/assets/{line-BX2Ugx3B.js → line-DVtVRzUA.js} +1 -1
- package/payload/server/public/assets/{linear-Bdc-QJWI.js → linear-DtoOFPO2.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-qv1e-J5x.js → mermaid-parser.core-G7YD_zO3.js} +2 -2
- package/payload/server/public/assets/{mermaid.core-C_d9YQX_.js → mermaid.core-DpFyndHm.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-DwKGvF14.js → mindmap-definition-QFDTVHPH-CYXK_Pvz.js} +1 -1
- package/payload/server/public/assets/{operator-BNY1P_mH.js → operator-CKmOgXNa.js} +1 -1
- package/payload/server/public/assets/{ordinal-BRQ5CzZY.js → ordinal-CBZeH4Yp.js} +1 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-DiU2yS9m.js +1 -0
- package/payload/server/public/assets/page-hG7xTrcO.js +32 -0
- package/payload/server/public/assets/pie-ZZUOXDRM-zHUKKlVv.js +1 -0
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-dUvQC_4S.js → pieDiagram-DEJITSTG-DFo4N6WH.js} +1 -1
- package/payload/server/public/assets/{public-CDzw6yjH.js → public-smrhkkxD.js} +1 -1
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-CebX_oPa.js → quadrantDiagram-34T5L4WZ-BlH5iIhl.js} +1 -1
- package/payload/server/public/assets/radar-PYXPWWZC-BUnVoaPg.js +1 -0
- package/payload/server/public/assets/{reduce-CkcXOmmJ.js → reduce-PuPlFPRX.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-h2euIqGj.js → requirementDiagram-MS252O5E-D5WLty8A.js} +1 -1
- package/payload/server/public/assets/rotate-ccw-Dd8aAvU7.js +1 -0
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-DCii3fkF.js → sankeyDiagram-XADWPNL6-vB85IxHG.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-CQSGYmlq.js → sequenceDiagram-FGHM5R23-Cxraa0Ee.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-CVtU0iXH.js → stateDiagram-FHFEXIEX-BBPssYBx.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-BWmmUwDt.js +1 -0
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-ChvsBltI.js → timeline-definition-GMOUNBTQ-Bgk6PT62.js} +1 -1
- package/payload/server/public/assets/treeView-SZITEDCU-C3Y-Q52h.js +1 -0
- package/payload/server/public/assets/treemap-W4RFUUIX-Dk0qZpAg.js +1 -0
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-e-EZiiba.js → vennDiagram-DHZGUBPP-B-APTs3L.js} +1 -1
- package/payload/server/public/assets/wardley-RL74JXVD-BAWI02OA.js +1 -0
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-4JaRn1s5.js → wardleyDiagram-NUSXRM2D-CkXJSs_i.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-kxwmCNuM.js → xychartDiagram-5P7HB3ND-BkyshPMV.js} +1 -1
- package/payload/server/public/browser.html +4 -4
- package/payload/server/public/calendar.html +4 -4
- package/payload/server/public/chat.html +7 -6
- package/payload/server/public/data.html +4 -4
- package/payload/server/public/graph.html +7 -6
- package/payload/server/public/index.html +6 -6
- package/payload/server/public/operator.html +9 -8
- package/payload/server/public/public.html +7 -6
- package/payload/server/server.js +255 -26
- package/payload/server/userprofile-reconcile-GLGKDVQQ.js +48 -0
- package/payload/server/public/assets/OperatorConversations-BJY9FZbr.js +0 -9
- package/payload/server/public/assets/OperatorConversations-C1I78hON.css +0 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-Csj53u5T.js +0 -1
- package/payload/server/public/assets/channel-BUQUAUts.js +0 -1
- package/payload/server/public/assets/chat-BlOWMUVI.js +0 -1
- package/payload/server/public/assets/chunk-426QAEUC-CdknReB2.js +0 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-8tHoEDE9.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-BEGZ3DLg.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-DsLxnm5U.js +0 -1
- package/payload/server/public/assets/clone-uiXbH_kh.js +0 -1
- package/payload/server/public/assets/data-p3T_EZFq.js +0 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-D_2KrZoP.js +0 -1
- package/payload/server/public/assets/info-OMHHGYJF-BP2TNwQ4.js +0 -1
- package/payload/server/public/assets/infoDiagram-42DDH7IO-D1kDtBOY.js +0 -2
- package/payload/server/public/assets/packet-4T2RLAQJ-BDg8fde8.js +0 -1
- package/payload/server/public/assets/page-CtygoIXt.js +0 -32
- package/payload/server/public/assets/pie-ZZUOXDRM-CXDyYMkZ.js +0 -1
- package/payload/server/public/assets/radar-PYXPWWZC-xen_zv8F.js +0 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-BywcXjtb.js +0 -1
- package/payload/server/public/assets/treeView-SZITEDCU-2FBf_1It.js +0 -1
- package/payload/server/public/assets/treemap-W4RFUUIX-BDdb30P-.js +0 -1
- package/payload/server/public/assets/wardley-RL74JXVD-BLq5MxjR.js +0 -1
- /package/payload/server/public/assets/{_baseFor-SRtUHe7G.js → _baseFor-brRjpUOX.js} +0 -0
- /package/payload/server/public/assets/{array-BpsnM-Py.js → array-BGFCBI0e.js} +0 -0
- /package/payload/server/public/assets/{cytoscape.esm-D4Rl1H5h.js → cytoscape.esm-TvRJ2tGX.js} +0 -0
- /package/payload/server/public/assets/{defaultLocale-C7sQPl-O.js → defaultLocale-CRZydyG6.js} +0 -0
- /package/payload/server/public/assets/{dist-CtUNt3w8.js → dist-B0CkUodR.js} +0 -0
- /package/payload/server/public/assets/{init-BydxaDEV.js → init-B8gtcn7T.js} +0 -0
- /package/payload/server/public/assets/{katex-B0fVeDx4.js → katex-RxgSu_dy.js} +0 -0
- /package/payload/server/public/assets/{path-jlWYQ2i9.js → path-DZF-JdEe.js} +0 -0
- /package/payload/server/public/assets/{preload-helper-uTix4PVD.js → preload-helper-CQ36nxok.js} +0 -0
- /package/payload/server/public/assets/{rough.esm-c4PR5shF.js → rough.esm-6CnTHTkH.js} +0 -0
- /package/payload/server/public/assets/{src-48kCpScq.js → src-CR_MU8uy.js} +0 -0
|
@@ -27,6 +27,20 @@ This is the entry point for every Cloudflare task on the install. Pick the opera
|
|
|
27
27
|
|
|
28
28
|
Neither the master token nor any per-scope token is ever written into a project tree, committed, or echoed into chat — the per-scope tokens persist only in the account-scoped secrets file, never in a deployable project tree.
|
|
29
29
|
|
|
30
|
+
### Token operations: run the helper first (binding)
|
|
31
|
+
|
|
32
|
+
Before **any** Cloudflare API token operation (verify, list, mint, reuse), resolve the per-scope token with the deterministic helper, never a hand-built `curl`:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
KEY=$(bash platform/plugins/cloudflare/bin/cf-token.sh <scope> <secrets-file>) # scope: pages | d1 | dns | access
|
|
36
|
+
set -a; . <secrets-file>; set +a
|
|
37
|
+
# the active, persisted per-scope token is now in the variable named by $KEY
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The helper owns the one decision the false-negative failure turns on: it reads the master's `cfat_`/`cfut_` prefix and routes verify, `permission_groups`, and mint to the matching endpoint family, mints once if absent (no expiry), verifies through the transient post-mint window, persists to the secrets file, and prints only the secrets key name. It never prints a token value.
|
|
41
|
+
|
|
42
|
+
A raw `curl` to a token endpoint chosen **before** the master's prefix is established is out of bounds. The wrong endpoint returns a stable false-negative that reads as "cannot mint" but is not: a `cfat_` master at `/user/tokens/verify` returns `Invalid API Token`, and a `cfut_` master at any `/accounts/{id}/...` endpoint returns `9109`. If you see either from a token probe, the cause is the endpoint, not the token. Stop and run the helper. Doing the verify or mint by hand is permitted only after reading the `references/api.md` section on routing token endpoints by prefix, and routing by the established prefix.
|
|
43
|
+
|
|
30
44
|
## Outcome contracts — what "done" means
|
|
31
45
|
|
|
32
46
|
Every Cloudflare operation proves itself with a live behavioural signal surfaced verbatim in chat:
|
|
@@ -33,7 +33,16 @@ There is never a `sites/<brand>/` versus `output/<brand>-site/` pair, a `.bak` c
|
|
|
33
33
|
|
|
34
34
|
## Auth: reuse the per-scope token, never mint ad hoc
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
Resolve the Pages-and-D1 token with the deterministic helper **before** any token `curl`, never a hand-built one:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
KEY=$(bash platform/plugins/cloudflare/bin/cf-token.sh pages <secrets-file>)
|
|
40
|
+
set -a; . <secrets-file>; set +a # the active token is now in the variable named by $KEY
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The helper reads the master's `cfat_`/`cfut_` prefix and routes verify, `permission_groups`, and mint to the matching endpoint family, mints once if absent (no expiry), persists, and prints only the secrets key name. A hand-built verify at the wrong endpoint returns a false-negative (`Invalid API Token` or `9109`) that reads as "cannot mint" but is not; when that happens the cause is the endpoint, not the token. See the `references/api.md` section on routing token endpoints by prefix.
|
|
44
|
+
|
|
45
|
+
A Pages deploy needs **Account · Cloudflare Pages · Edit**. A site with a D1-backed form also needs **Account · D1 · Edit** on the same token. The helper resolves the stable `<brand>-pages-d1` token (carrying both) from the account secrets file, minting it once only if absent and persisting it, exactly as `references/api.md` § Provisioning and reusing a stable per-scope token and `references/d1-data-capture.md` § 0 prescribe. The token is never written into the project tree, never committed, never echoed into chat. The master token stays in the secrets file. Surface every command as verb plus target ("deploying Pages project `<project>`"); `wrangler` prints the deployment URL, which is what gets relayed.
|
|
37
46
|
|
|
38
47
|
## Deploy and attach the custom domain
|
|
39
48
|
|
|
@@ -37,6 +37,7 @@ Load these when performing admin tasks or diagnosing platform behaviour:
|
|
|
37
37
|
- **Admin UI** → `references/admin-ui.md` — compact map of every `/api/admin/*` mount, every sidebar surface, the system-stats widget (CPU/RAM thresholds), the artefact pane, and the health-vs-version split. Load when diagnosing an admin route, explaining the CPU/RAM widget, or onboarding a new admin endpoint.
|
|
38
38
|
- **Admin session** → `references/admin-session.md` — signed sessionKey + PIN-rebind survival contract, SDK-resume across `systemctl restart`, and the single-entry `POST /api/admin/claude-sessions` wrapper that owns every programmatic admin spawn-with-initialMessage. Load when diagnosing admin session continuity, designing a programmatic admin spawn.
|
|
39
39
|
- **Cloudflare** → `references/cloudflare.md` — dashboard-first tunnel setup, the `cloudflared`-CLI-only tool surface, single-recovery-path (re-login) for every wrong-account failure.
|
|
40
|
+
- **claude.ai connectors** → `references/claudeai-connectors.md` — why a connector added on claude.ai can still need authentication (per-client, not per-account), and the in-chat authenticate path (the agent issues a link, the operator approves on claude.ai, the session reconnects). Separate from the Maxy connector plugin.
|
|
40
41
|
- **Deployment** → `references/deployment.md` — Pi setup, Cloudflare tunnel, start script
|
|
41
42
|
|
|
42
43
|
## References
|
|
@@ -56,4 +57,5 @@ Load these when performing admin tasks or diagnosing platform behaviour:
|
|
|
56
57
|
- references/admin-ui.md
|
|
57
58
|
- references/admin-session.md
|
|
58
59
|
- references/cloudflare.md
|
|
60
|
+
- references/claudeai-connectors.md
|
|
59
61
|
- references/deployment.md
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# claude.ai connectors
|
|
2
|
+
|
|
3
|
+
claude.ai connectors (Zoom, Dropbox, GitHub, Google Calendar, and the rest you add on claude.ai) work
|
|
4
|
+
inside your admin chat once they are authorised for this Maxy client. This page explains why a
|
|
5
|
+
connector you already added can still say it needs authentication, and how to fix it without leaving
|
|
6
|
+
the chat.
|
|
7
|
+
|
|
8
|
+
## Asking for a connector by name
|
|
9
|
+
|
|
10
|
+
You can ask the agent to use a connector you added on claude.ai by name — "can we connect my Zoom
|
|
11
|
+
account?", "save this to Dropbox", "check my Microsoft 365 calendar". The agent knows which connectors
|
|
12
|
+
are loaded for this client and whether each one is ready or still needs the one-off authentication
|
|
13
|
+
below, so it will reach for the connector rather than telling you it has no such integration. If a
|
|
14
|
+
connector you added is not yet authenticated for this client, the agent offers you the authentication
|
|
15
|
+
step straight away instead of dead-ending.
|
|
16
|
+
|
|
17
|
+
## Why a connector can still need authentication
|
|
18
|
+
|
|
19
|
+
A claude.ai connector is authorised per client, not per account. Approving a connector in the
|
|
20
|
+
claude.ai web app authorises the web app only. Your Maxy admin chat is a separate client, so the first
|
|
21
|
+
time it uses a connector that client has to be authorised too. This is a one-off step per connector.
|
|
22
|
+
|
|
23
|
+
That is why some connectors work straight away while a newly added one does not. A connector you used
|
|
24
|
+
before was already authorised for this client; a brand new one has only been approved in the web app.
|
|
25
|
+
|
|
26
|
+
## How to authenticate one from the chat
|
|
27
|
+
|
|
28
|
+
When the agent tries a connector that has not been authorised for this client, it will tell you the
|
|
29
|
+
connector needs authentication and hand you a link. You do not need a terminal and you do not need to
|
|
30
|
+
run anything yourself.
|
|
31
|
+
|
|
32
|
+
1. The agent gives you an authorisation link.
|
|
33
|
+
2. Open the link and approve the connector on claude.ai (sign in with the same account you use for
|
|
34
|
+
your connectors).
|
|
35
|
+
3. Tell the agent you have approved it.
|
|
36
|
+
4. The agent reconnects your chat session. Send your next message and the connector is ready to use.
|
|
37
|
+
|
|
38
|
+
If a connector still reports that it needs authentication after you have approved it and the session
|
|
39
|
+
has reconnected, that is unexpected. Tell your installer so they can look into it.
|
|
40
|
+
|
|
41
|
+
## What this is not
|
|
42
|
+
|
|
43
|
+
This is about Anthropic's claude.ai connectors, which authenticate per client. It is separate from the
|
|
44
|
+
Maxy connector plugin, which registers ordinary REST APIs with credentials Maxy holds. The two are
|
|
45
|
+
unrelated.
|
|
@@ -93,6 +93,12 @@ The most common cause is wrong nameservers on the domain. The domain must use Cl
|
|
|
93
93
|
- 5 failed login attempts → 15-minute lockout — wait for expiry.
|
|
94
94
|
- The remote password is set during Cloudflare Tunnel onboarding — the agent asks for one in chat and stores it deterministically. The browser form at `/__remote-auth/setup` remains available for resets on the local network.
|
|
95
95
|
|
|
96
|
+
### Large `/data` upload fails with a connection error
|
|
97
|
+
|
|
98
|
+
- Cloudflare proxies a request body of at most ~100 MB on the current plan. A single upload larger than that is cut at the edge before it reaches the server, so the browser sees a connection error rather than a clean size rejection.
|
|
99
|
+
- The `/data` page handles this itself: files over 95 MB are split into ~48 MB parts and sent one at a time under the cap, and the server reassembles them. No plan change or dashboard action is needed for uploads up to the 2 GB app ceiling.
|
|
100
|
+
- Raising the proxied body limit is a paid-plan change made on the Cloudflare dashboard, not something the agent does.
|
|
101
|
+
|
|
96
102
|
## What the agent does and does not do
|
|
97
103
|
|
|
98
104
|
**Does:** invokes `cloudflared` directly via Bash, following `plugins/cloudflare/references/manual-setup.md` step by step; quotes click-paths from the reference files verbatim; verifies external reachability with `curl -I` and surfaces the response.
|
|
@@ -125,6 +125,8 @@ tail -200 ~/.maxy/logs/maxy-ui.log | rg '\[remote-auth\].*resolvedKind='
|
|
|
125
125
|
|
|
126
126
|
**If a background task goes silent and the chat shows "A background task went silent — K of M completed":** {{productName}}'s subagent stopped emitting progress for over 2 minutes. Tap "Continue" — the next turn resumes the prior session and reads a synthetic tool_result describing what completed before the pause, so the agent re-plans without losing the work it had done. Most stalls are upstream API latency rather than the subagent's approach failing — the resume-first path treats both correctly. Greppable post-deploy invariants: `[stall-recovery] kind=subagent_stalled … completed=<K>/? handoff=resume-first` followed by `[stall-resume] consumed kind=subagent_stalled toolUseId=<8>` on the next turn. If the button reads "Start over" instead, the parent's pending tool_use_id was not captured — the fallback path took over; the prior conversation is preserved as a `<recovery-context>` block in the cold-started session.
|
|
127
127
|
|
|
128
|
+
**The stop button is stuck and the spinner never clears.** If the chat shows a stop button and a working spinner that never resolves, and the session dot is grey (not live), the agent process for that session was killed mid-turn (most often the device ran low on memory). There is no live turn to interrupt, so the old behaviour left you stranded. Now it self-heals: within a few seconds the spinner clears on its own and the composer shows "The agent stopped. Send a message to resume." Pressing stop clears it immediately. Either way, just send your next message — the session resumes from where it left off. Greppable post-deploy invariants: the manager logs `[rc-life] op=turn-aborted reason=child-exit-mid-turn` on the death and `[webchat-interrupt] op=dead-child-recovered` if you press stop; the browser console logs `[admin-ui] op=phantom-turn-cleared` (self-heal) or `[admin-ui] op=stop-dead-child` (stop press). If a session keeps dying mid-turn, the device is memory-constrained — that is a capacity concern, separate from this recovery.
|
|
129
|
+
|
|
128
130
|
**Agent searches the filesystem after uploading a zip.** If you uploaded a zip and the agent burns several turns running `find` / `Glob` instead of unzipping, that is the symptom of the recovery-retry attachment-context regression (now closed by the recovery context preservation contract in `.docs/agents.md`). Greppable confirmation is the `[context-overflow-recovery] retry … attachmentsCarried=<n>` line in the conversation stream log. If you see `[context-overflow-recovery] WARN attachment-context-lost`, the regression has returned — surface to support.
|
|
129
131
|
|
|
130
132
|
**Turn budget exhausted with a horizontal rule separating two assistant turns.** When {{productName}} reaches its turn budget and the doubled retry also runs out, the chat now shows a one-paragraph assistant message that opens with `error_max_turns turns=A→B` (initial budget → final budget) followed by the recovery copy: "I reached my turn budget of N before I could finish this request. Try sending a smaller or more focused request, or ask me to use higher effort." That message is persisted to the graph, so the next page-refresh still shows it. The thin horizontal rule labelled "Session restored after timeout." that appears above your following turn signals that the prior turn forced a cold SDK-session restart inside the same conversation (pool eviction) — the agent's response after the rule is from a fresh SDK session even though the conversation thread is unchanged. Greppable post-deploy invariants: `[context-overflow-recovery] exhausted cause=max-turns-interrupted` count equals `[admin-persist] writer=persistMessageExhaust outcome=ok` count for the same sessionId window, and one `[session-store] storeAgentSessionId` line marks the cold-restart that drove the on-screen rule.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "email",
|
|
3
|
-
"description": "Email — dedicated email account. Retrieval: email-graph-query for stored email-thread history as :ConversationArchive {source:'email'} with :Section chunks (recall, search, 'threads about X', 'what did Y send?'); email-read / email-search for live IMAP inbox (with best-effort per-archive section hints from the graph). Ingestion (operator-initiated, human-in-the-loop): email-fetch lists new messages, email-ingest writes the operator-approved subset via the conversation-archive pipeline. Sending: email-send (new), email-reply (threaded — resolves the original via IMAP Message-ID search), email-draft (compose to the mailbox Drafts folder, unsent, for operator review; pass messageId for a threaded draft-reply), email-draft-edit (revise a stored draft by Drafts UID), email-draft-send (dispatch a stored draft by Drafts UID, then remove it). Config: email-provider-info (pre-password host + app-password lookup from an address alone), email-setup, email-status, email-otp-extract.",
|
|
3
|
+
"description": "Email — dedicated email account. Retrieval: email-graph-query for stored email-thread history as :ConversationArchive {source:'email'} with :Section chunks (recall, search, 'threads about X', 'what did Y send?'); email-read / email-search for live IMAP inbox previews (with best-effort per-archive section hints from the graph); email-fetch-body for the COMPLETE decoded body of one message (by uid or message_id) when a preview is not enough. Ingestion (operator-initiated, human-in-the-loop): email-fetch lists new messages, email-ingest writes the operator-approved subset via the conversation-archive pipeline. Sending: email-send (new), email-reply (threaded — resolves the original via IMAP Message-ID search), email-draft (compose to the mailbox Drafts folder, unsent, for operator review; pass messageId for a threaded draft-reply), email-draft-edit (revise a stored draft, located by its Drafts UID or, when Gmail has renumbered it, its draft Message-ID), email-draft-send (dispatch a stored draft by Drafts UID, then remove it). Config: email-provider-info (pre-password host + app-password lookup from an address alone), email-setup, email-status, email-otp-extract.",
|
|
4
4
|
"version": "0.1.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Rubytech LLC"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: email
|
|
3
|
-
description: "Email — dedicated email account. Retrieval: email-graph-query for stored email-thread history as :ConversationArchive {source:'email'} with :Section chunks (recall, search, 'threads about X', 'what did Y send?'); email-read / email-search for live IMAP inbox (with best-effort per-archive section hints from the graph). Ingestion (operator-initiated, human-in-the-loop): email-fetch lists new messages, email-ingest writes the operator-approved subset via the conversation-archive pipeline. Sending: email-send (new), email-reply (threaded — resolves the original via IMAP Message-ID search), email-draft (compose to the mailbox Drafts folder, unsent, for operator review; pass messageId for a threaded draft-reply), email-draft-edit (revise a stored draft by Drafts UID), email-draft-send (dispatch a stored draft by Drafts UID, then remove it). Config: email-provider-info (pre-password host + app-password lookup from an address alone), email-setup, email-status, email-otp-extract."
|
|
3
|
+
description: "Email — dedicated email account. Retrieval: email-graph-query for stored email-thread history as :ConversationArchive {source:'email'} with :Section chunks (recall, search, 'threads about X', 'what did Y send?'); email-read / email-search for live IMAP inbox previews (with best-effort per-archive section hints from the graph); email-fetch-body for the COMPLETE decoded body of one message (by uid or message_id) when a preview is not enough. Ingestion (operator-initiated, human-in-the-loop): email-fetch lists new messages, email-ingest writes the operator-approved subset via the conversation-archive pipeline. Sending: email-send (new), email-reply (threaded — resolves the original via IMAP Message-ID search), email-draft (compose to the mailbox Drafts folder, unsent, for operator review; pass messageId for a threaded draft-reply), email-draft-edit (revise a stored draft, located by its Drafts UID or, when Gmail has renumbered it, its draft Message-ID), email-draft-send (dispatch a stored draft by Drafts UID, then remove it). Config: email-provider-info (pre-password host + app-password lookup from an address alone), email-setup, email-status, email-otp-extract."
|
|
4
4
|
tools:
|
|
5
5
|
- name: email-provider-info
|
|
6
6
|
publicAllowlist: false
|
|
@@ -11,6 +11,9 @@ tools:
|
|
|
11
11
|
- name: email-read
|
|
12
12
|
publicAllowlist: false
|
|
13
13
|
adminAllowlist: false
|
|
14
|
+
- name: email-fetch-body
|
|
15
|
+
publicAllowlist: false
|
|
16
|
+
adminAllowlist: false
|
|
14
17
|
- name: email-send
|
|
15
18
|
publicAllowlist: false
|
|
16
19
|
adminAllowlist: false
|
|
@@ -75,10 +78,11 @@ Manages the agent's own dedicated email account — IMAP for reading, SMTP for s
|
|
|
75
78
|
- **Setup:** `email-setup` — collect credentials in plain chat and connect IMAP/SMTP. Hosts are auto-inferred for known providers; supports alias addresses (`agentAddress`).
|
|
76
79
|
- **Read inbox:** `email-read` — metadata only (UID, sender, subject, date), plus a best-effort `Archive`/`Archive-Section` hint when the IMAP message's `receivedAt` falls inside a stored `:ConversationArchive {source:'email'}` section, plus an `Attachments (N): filename (mime, size); …` footer when the message carries enclosures. Supports pagination (`before_uid`), folders (`inbox`/`sent`), filtering by sender/date/subject. Metadata is derived from IMAP `bodyStructure` — no bytes downloaded.
|
|
77
80
|
- **Search inbox:** `email-search` — live IMAP search by sender, subject, body keyword, date range. Same per-archive hint and attachment footer as `email-read`.
|
|
81
|
+
- **Read one whole message:** `email-fetch-body` — the COMPLETE decoded body of a single message addressed by `uid` (with optional `folder`) or `message_id` (resolved across inbox and sent). No 500-char preview cap. Downloads the full RFC822 source and decodes it with `mailparser`, so every MIME shape — flat text, single-part HTML, `multipart/alternative`, and nested `multipart/related`/`multipart/mixed` (Outlook's inline-image house style) — yields a body; the preview tools above intentionally stay preview-only. Use this when the operator wants to read or act on a message's actual content.
|
|
78
82
|
- **Send:** `email-send` — new outbound email. Pass `cc`/`bcc` (each an array of email addresses) to copy or blind-copy recipients — CC appears in the message header, BCC recipients receive the message without appearing in any header. Pass `attachments` (absolute file paths under the account directory, ≤25 MB each) to attach files as SMTP attachments; an invalid path fails the send with no email dispatched. The success message names the CC recipients and reports the BCC count without naming them.
|
|
79
83
|
- **Reply:** `email-reply` — threaded reply to an existing email by `messageId`. Resolves the original envelope via IMAP `SEARCH HEADER Message-ID` (INBOX then `\Sent`), so threading works for any message still present on the server even if the graph never ingested it. Supports `replyAll`, `cc`/`bcc`, and `attachments` (same path rules as `email-send`). Explicit `cc`/`bcc` are added in addition to whatever `replyAll` places in `To`, passed through as given (no overlap de-duplication against `To`).
|
|
80
|
-
- **Compose to Drafts:** `email-draft` — builds the same MIME message as `email-send` (same `cc`/`bcc`/`attachments` rules; unlike a dispatched send, the stored draft keeps its `Bcc:` header, because the draft bytes are the only recipient carrier) but APPENDs it to the mailbox's `\Drafts` special-use folder with the `\Draft` flag instead of dispatching it. Nothing is sent. Pass `messageId` to compose a threaded draft-reply: the parent is resolved via IMAP and the draft carries `In-Reply-To`/`References` plus a `Re:` subject from the parent, exactly like `email-reply` (the `subject` argument is then optional and ignored). The success message names the Drafts folder
|
|
81
|
-
- **Revise a draft:** `email-draft-edit` — replace a stored draft
|
|
84
|
+
- **Compose to Drafts:** `email-draft` — builds the same MIME message as `email-send` (same `cc`/`bcc`/`attachments` rules; unlike a dispatched send, the stored draft keeps its `Bcc:` header, because the draft bytes are the only recipient carrier) but APPENDs it to the mailbox's `\Drafts` special-use folder with the `\Draft` flag instead of dispatching it. Nothing is sent. Pass `messageId` to compose a threaded draft-reply: the parent is resolved via IMAP and the draft carries `In-Reply-To`/`References` plus a `Re:` subject from the parent, exactly like `email-reply` (the `subject` argument is then optional and ignored). The success message names the Drafts folder, the APPEND UID, and the draft's own `Message-ID` (`draft id`) so a saved draft is never indistinguishable from a silent no-op and a later edit has a stable handle. Fails naming the mailbox when the server advertises no `\Drafts` special-use folder — no fallback folder is picked. Use when the operator wants to review and send by hand.
|
|
85
|
+
- **Revise a draft:** `email-draft-edit` — replace a stored draft. The `targetUid` is a fast-path hint; the draft's own `Message-ID` (`draftMessageId`) is the stable identity, because Gmail renumbers a draft's UID when it is touched. The tool tries the UID first, then relocates by a `Message-ID` header search, then composes the replacement from the parameters (same shape as `email-draft`, plus `targetUid`, optional `draftMessageId`, and an optional `messageId` for a threaded revision), APPENDs it with `\Draft`, and deletes the resolved old copy — the replacement is always appended before the old copy is expunged, so a mid-cycle failure never leaves zero copies. When neither identity resolves a live draft, the prior draft is gone: the replacement is saved as a fresh draft (`created`) rather than failing. A single transient transport drop is retried on a fresh connection; auth and other permanent errors surface immediately. Nothing is sent. The confirmation names the folder, the new UID, and the new draft id.
|
|
82
86
|
- **Send a draft:** `email-draft-send` — dispatch a stored draft addressed by its Drafts UID via SMTP, then remove it from Drafts. Recipients come from the draft's own To/Cc/Bcc headers (the `\Drafts` copy keeps its `Bcc:`), deduped, sent from the agent alias with an explicit SMTP envelope. The draft is deleted only after SMTP accepts; an SMTP failure leaves it in place and surfaces verbatim. A draft with no recipients is refused before sending. Operator-approved sends only.
|
|
83
87
|
- **Ingestion — fetch:** `email-fetch` — list new IMAP messages since the stored high-water mark. Metadata + body preview only — including attachment names, mimetypes, and sizes so the operator sees what's attached before approving. No bytes downloaded, no graph writes. Stages the batch in-memory for `email-ingest`.
|
|
84
88
|
- **Ingestion — apply:** `email-ingest` — apply the operator's per-message decisions to the staged batch. Each Message-ID must carry a disposition (`ingest` or `discard`). Approved messages land on `:ConversationArchive {source:'email'}` via the source-agnostic conversation-archive pipeline (one archive per thread, sessionised into `:Section` chunks). Attachment bytes are fetched, archived to `{accountDir}/archive/email/<uidValidity>-<uid>/<sha256>-<filename>` (0o600, 25 MB cap), and each attachment lands as a content-addressed `:DigitalDocument` keyed on `attachmentId = sha256(bytes)`; the `:HAS_ENCLOSURE` edge from the parent `:ConversationArchive` is wired by `memory-ingest` on its next pass via the `pendingArchiveEdges` queue. Advances the inbox high-water mark only after a successful write.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"body-extract.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/body-extract.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { extractBody, previewBody } from "../lib/imap.js";
|
|
3
|
+
const CRLF = "\r\n";
|
|
4
|
+
const plain = [
|
|
5
|
+
"From: a@x.com",
|
|
6
|
+
"To: b@x.com",
|
|
7
|
+
"Subject: Hi",
|
|
8
|
+
"Content-Type: text/plain; charset=utf-8",
|
|
9
|
+
"",
|
|
10
|
+
"Hello plain world.",
|
|
11
|
+
"",
|
|
12
|
+
].join(CRLF);
|
|
13
|
+
const alternative = [
|
|
14
|
+
"From: a@x.com",
|
|
15
|
+
"Subject: Alt",
|
|
16
|
+
'Content-Type: multipart/alternative; boundary="B"',
|
|
17
|
+
"",
|
|
18
|
+
"--B",
|
|
19
|
+
"Content-Type: text/plain; charset=utf-8",
|
|
20
|
+
"",
|
|
21
|
+
"Plain wins.",
|
|
22
|
+
"",
|
|
23
|
+
"--B",
|
|
24
|
+
"Content-Type: text/html; charset=utf-8",
|
|
25
|
+
"",
|
|
26
|
+
"<p>HTML loses.</p>",
|
|
27
|
+
"",
|
|
28
|
+
"--B--",
|
|
29
|
+
"",
|
|
30
|
+
].join(CRLF);
|
|
31
|
+
// Outlook house style: multipart/related → nested multipart/alternative
|
|
32
|
+
// carrying ONLY text/html (no top-level text/plain) + an inline image part.
|
|
33
|
+
// The hand-rolled extractor returned "(body unavailable)" for this shape.
|
|
34
|
+
const outlook = [
|
|
35
|
+
"From: max@outlook.com",
|
|
36
|
+
"Subject: Proposal",
|
|
37
|
+
'Content-Type: multipart/related; boundary="R"',
|
|
38
|
+
"",
|
|
39
|
+
"--R",
|
|
40
|
+
'Content-Type: multipart/alternative; boundary="A"',
|
|
41
|
+
"",
|
|
42
|
+
"--A",
|
|
43
|
+
"Content-Type: text/html; charset=utf-8",
|
|
44
|
+
"",
|
|
45
|
+
"<p>Real Outlook <b>body</b> text.</p>",
|
|
46
|
+
"",
|
|
47
|
+
"--A--",
|
|
48
|
+
"",
|
|
49
|
+
"--R",
|
|
50
|
+
"Content-Type: image/png",
|
|
51
|
+
"Content-Transfer-Encoding: base64",
|
|
52
|
+
"",
|
|
53
|
+
"iVBORw0KGgo=",
|
|
54
|
+
"",
|
|
55
|
+
"--R--",
|
|
56
|
+
"",
|
|
57
|
+
].join(CRLF);
|
|
58
|
+
const longText = "x".repeat(1200);
|
|
59
|
+
const longMsg = [
|
|
60
|
+
"Subject: Long",
|
|
61
|
+
"Content-Type: text/plain; charset=utf-8",
|
|
62
|
+
"",
|
|
63
|
+
longText,
|
|
64
|
+
"",
|
|
65
|
+
].join(CRLF);
|
|
66
|
+
describe("extractBody", () => {
|
|
67
|
+
it("decodes a flat text/plain body", async () => {
|
|
68
|
+
const r = await extractBody(plain);
|
|
69
|
+
expect(r.body).toBe("Hello plain world.");
|
|
70
|
+
expect(r.kind).toBe("text");
|
|
71
|
+
});
|
|
72
|
+
it("prefers text/plain in multipart/alternative", async () => {
|
|
73
|
+
const r = await extractBody(alternative);
|
|
74
|
+
expect(r.body).toContain("Plain wins.");
|
|
75
|
+
expect(r.kind).toBe("text");
|
|
76
|
+
});
|
|
77
|
+
it("yields the body for nested multipart/related html-only (Outlook regression guard)", async () => {
|
|
78
|
+
const r = await extractBody(outlook);
|
|
79
|
+
expect(r.body).toContain("Real Outlook");
|
|
80
|
+
expect(r.body).toContain("body");
|
|
81
|
+
expect(r.body).not.toBe("(body unavailable)");
|
|
82
|
+
expect(r.kind).toBe("html");
|
|
83
|
+
});
|
|
84
|
+
it("returns the whole long body uncapped", async () => {
|
|
85
|
+
const r = await extractBody(longMsg);
|
|
86
|
+
expect(r.body.length).toBeGreaterThanOrEqual(1200);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
describe("previewBody", () => {
|
|
90
|
+
it("truncates a >500-char body with an ellipsis", () => {
|
|
91
|
+
const p = previewBody("y".repeat(1200));
|
|
92
|
+
expect(p.length).toBe(501);
|
|
93
|
+
expect(p.endsWith("…")).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
it("passes short bodies through unchanged", () => {
|
|
96
|
+
expect(previewBody("short")).toBe("short");
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
//# sourceMappingURL=body-extract.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"body-extract.test.js","sourceRoot":"","sources":["../../src/__tests__/body-extract.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compose-draft-id.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/compose-draft-id.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { composeDraft } from "../lib/compose.js";
|
|
3
|
+
// Standalone (non-threaded) compose touches no IMAP and no filesystem when
|
|
4
|
+
// there are no attachments, so it can be exercised directly.
|
|
5
|
+
const config = {
|
|
6
|
+
email: "login@example.com",
|
|
7
|
+
agentAddress: "agent@example.com",
|
|
8
|
+
};
|
|
9
|
+
describe("Task 1177 — composeDraft stamps a stable draft Message-ID", () => {
|
|
10
|
+
it("returns a draftMessageId on the agent-address domain", async () => {
|
|
11
|
+
const composed = await composeDraft(config, "pw", "acc-1", {
|
|
12
|
+
to: "c@d.e",
|
|
13
|
+
subject: "Quote",
|
|
14
|
+
body: "Body",
|
|
15
|
+
});
|
|
16
|
+
expect(composed.draftMessageId).toMatch(/^<.+@example\.com>$/);
|
|
17
|
+
});
|
|
18
|
+
it("stamps the same Message-ID into the composed raw bytes", async () => {
|
|
19
|
+
const composed = await composeDraft(config, "pw", "acc-1", {
|
|
20
|
+
to: "c@d.e",
|
|
21
|
+
subject: "Quote",
|
|
22
|
+
body: "Body",
|
|
23
|
+
});
|
|
24
|
+
expect(composed.raw.toString()).toContain(`Message-ID: ${composed.draftMessageId}`);
|
|
25
|
+
});
|
|
26
|
+
it("gives each composed draft its own unique Message-ID", async () => {
|
|
27
|
+
const a = await composeDraft(config, "pw", "acc-1", { to: "c@d.e", subject: "Q", body: "1" });
|
|
28
|
+
const b = await composeDraft(config, "pw", "acc-1", { to: "c@d.e", subject: "Q", body: "2" });
|
|
29
|
+
expect(a.draftMessageId).not.toBe(b.draftMessageId);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
//# sourceMappingURL=compose-draft-id.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compose-draft-id.test.js","sourceRoot":"","sources":["../../src/__tests__/compose-draft-id.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGjD,2EAA2E;AAC3E,6DAA6D;AAC7D,MAAM,MAAM,GAAG;IACb,KAAK,EAAE,mBAAmB;IAC1B,YAAY,EAAE,mBAAmB;CACnB,CAAC;AAEjB,QAAQ,CAAC,2DAA2D,EAAE,GAAG,EAAE;IACzE,EAAE,CAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;QACpE,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE;YACzD,EAAE,EAAE,OAAO;YACX,OAAO,EAAE,OAAO;YAChB,IAAI,EAAE,MAAM;SACb,CAAC,CAAC;QACH,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;QACtE,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE;YACzD,EAAE,EAAE,OAAO;YACX,OAAO,EAAE,OAAO;YAChB,IAAI,EAAE,MAAM;SACb,CAAC,CAAC;QACH,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,SAAS,CAAC,eAAe,QAAQ,CAAC,cAAc,EAAE,CAAC,CAAC;IACtF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;QACnE,MAAM,CAAC,GAAG,MAAM,YAAY,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QAC9F,MAAM,CAAC,GAAG,MAAM,YAAY,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QAC9F,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/credentials.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
2
|
+
import { mkdtempSync, rmSync, writeFileSync, readFileSync, mkdirSync, existsSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
// credentials.ts imports neo4j-driver + ./neo4j.js at module load; stub the
|
|
6
|
+
// graph surface so the password-store functions can be exercised in isolation.
|
|
7
|
+
vi.mock("../lib/neo4j.js", () => ({ getSession: vi.fn() }));
|
|
8
|
+
// homedir is the legacy-migration anchor; redirect it to a per-test temp dir.
|
|
9
|
+
// ESM exports can't be spied, so mock the module with a mutable home pointer.
|
|
10
|
+
const osState = vi.hoisted(() => ({ home: "" }));
|
|
11
|
+
vi.mock("node:os", async (importOriginal) => {
|
|
12
|
+
const actual = await importOriginal();
|
|
13
|
+
return { ...actual, homedir: () => osState.home };
|
|
14
|
+
});
|
|
15
|
+
let brandA;
|
|
16
|
+
let brandB;
|
|
17
|
+
let legacyHome;
|
|
18
|
+
async function freshModule() {
|
|
19
|
+
vi.resetModules();
|
|
20
|
+
return await import("../lib/credentials.js");
|
|
21
|
+
}
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
brandA = mkdtempSync(join(tmpdir(), "brandA-"));
|
|
24
|
+
brandB = mkdtempSync(join(tmpdir(), "brandB-"));
|
|
25
|
+
legacyHome = mkdtempSync(join(tmpdir(), "home-"));
|
|
26
|
+
osState.home = legacyHome;
|
|
27
|
+
});
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
vi.restoreAllMocks();
|
|
30
|
+
for (const d of [brandA, brandB, legacyHome])
|
|
31
|
+
rmSync(d, { recursive: true, force: true });
|
|
32
|
+
delete process.env.PLATFORM_ROOT;
|
|
33
|
+
});
|
|
34
|
+
describe("per-mailbox, per-brand password store", () => {
|
|
35
|
+
it("round-trips a password scoped to an email under one brand root", async () => {
|
|
36
|
+
process.env.PLATFORM_ROOT = brandA;
|
|
37
|
+
const cred = await freshModule();
|
|
38
|
+
cred.writePassword("info@rubytech.llc", "secretA");
|
|
39
|
+
expect(cred.readPassword("info@rubytech.llc")).toBe("secretA");
|
|
40
|
+
expect(existsSync(join(brandA, "config/email-credentials.json"))).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
it("keeps two mailboxes under one brand independent", async () => {
|
|
43
|
+
process.env.PLATFORM_ROOT = brandA;
|
|
44
|
+
const cred = await freshModule();
|
|
45
|
+
cred.writePassword("a@x.com", "pwA");
|
|
46
|
+
cred.writePassword("b@x.com", "pwB");
|
|
47
|
+
expect(cred.readPassword("a@x.com")).toBe("pwA");
|
|
48
|
+
expect(cred.readPassword("b@x.com")).toBe("pwB");
|
|
49
|
+
});
|
|
50
|
+
it("does not let brand B's write touch brand A's store", async () => {
|
|
51
|
+
process.env.PLATFORM_ROOT = brandA;
|
|
52
|
+
let cred = await freshModule();
|
|
53
|
+
cred.writePassword("shared@x.com", "pwA");
|
|
54
|
+
process.env.PLATFORM_ROOT = brandB;
|
|
55
|
+
cred = await freshModule();
|
|
56
|
+
cred.writePassword("shared@x.com", "pwB");
|
|
57
|
+
expect(cred.readPassword("shared@x.com")).toBe("pwB");
|
|
58
|
+
process.env.PLATFORM_ROOT = brandA;
|
|
59
|
+
cred = await freshModule();
|
|
60
|
+
expect(cred.readPassword("shared@x.com")).toBe("pwA");
|
|
61
|
+
});
|
|
62
|
+
it("throws naming the active email and stored identities when the key is absent", async () => {
|
|
63
|
+
process.env.PLATFORM_ROOT = brandA;
|
|
64
|
+
const cred = await freshModule();
|
|
65
|
+
cred.writePassword("dale@sitedesk.online", "pw");
|
|
66
|
+
expect(() => cred.readPassword("info@rubytech.llc")).toThrow(/info@rubytech\.llc/);
|
|
67
|
+
expect(() => cred.readPassword("info@rubytech.llc")).toThrow(/dale@sitedesk\.online/);
|
|
68
|
+
});
|
|
69
|
+
it("removePassword deletes only the named entry", async () => {
|
|
70
|
+
process.env.PLATFORM_ROOT = brandA;
|
|
71
|
+
const cred = await freshModule();
|
|
72
|
+
cred.writePassword("a@x.com", "pwA");
|
|
73
|
+
cred.writePassword("b@x.com", "pwB");
|
|
74
|
+
cred.removePassword("a@x.com");
|
|
75
|
+
expect(() => cred.readPassword("a@x.com")).toThrow();
|
|
76
|
+
expect(cred.readPassword("b@x.com")).toBe("pwB");
|
|
77
|
+
});
|
|
78
|
+
it("migrates the legacy ~/.maxy/.email-password onto the configured email on first read", async () => {
|
|
79
|
+
mkdirSync(join(legacyHome, ".maxy"), { recursive: true });
|
|
80
|
+
writeFileSync(join(legacyHome, ".maxy/.email-password"), "legacySecret");
|
|
81
|
+
process.env.PLATFORM_ROOT = brandA;
|
|
82
|
+
const cred = await freshModule();
|
|
83
|
+
expect(cred.readPassword("info@rubytech.llc")).toBe("legacySecret");
|
|
84
|
+
const store = JSON.parse(readFileSync(join(brandA, "config/email-credentials.json"), "utf-8"));
|
|
85
|
+
expect(store["info@rubytech.llc"]).toBe("legacySecret");
|
|
86
|
+
// legacy file untouched
|
|
87
|
+
expect(readFileSync(join(legacyHome, ".maxy/.email-password"), "utf-8")).toBe("legacySecret");
|
|
88
|
+
});
|
|
89
|
+
it("fails loudly on an empty stored value and never adopts the legacy secret over an existing key", async () => {
|
|
90
|
+
// A legacy file IS present — it must NOT be adopted when the brand store
|
|
91
|
+
// already holds the key (even with an empty value). Adopting it would be a
|
|
92
|
+
// cross-brand secret read, the exact breach this task closes.
|
|
93
|
+
mkdirSync(join(legacyHome, ".maxy"), { recursive: true });
|
|
94
|
+
writeFileSync(join(legacyHome, ".maxy/.email-password"), "foreignSecret");
|
|
95
|
+
process.env.PLATFORM_ROOT = brandA;
|
|
96
|
+
mkdirSync(join(brandA, "config"), { recursive: true });
|
|
97
|
+
writeFileSync(join(brandA, "config/email-credentials.json"), JSON.stringify({ "info@rubytech.llc": "" }), { mode: 0o600 });
|
|
98
|
+
const cred = await freshModule();
|
|
99
|
+
expect(() => cred.readPassword("info@rubytech.llc")).toThrow(/empty/i);
|
|
100
|
+
const store = JSON.parse(readFileSync(join(brandA, "config/email-credentials.json"), "utf-8"));
|
|
101
|
+
expect(store["info@rubytech.llc"]).toBe("");
|
|
102
|
+
});
|
|
103
|
+
it("credentialStorePath resolves under the brand root", async () => {
|
|
104
|
+
process.env.PLATFORM_ROOT = brandA;
|
|
105
|
+
const cred = await freshModule();
|
|
106
|
+
expect(cred.credentialStorePath()).toBe(join(brandA, "config/email-credentials.json"));
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
//# sourceMappingURL=credentials.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.test.js","sourceRoot":"","sources":["../../src/__tests__/credentials.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,9 +4,17 @@ const { composeMock, replaceMock } = vi.hoisted(() => ({
|
|
|
4
4
|
raw: Buffer.from("MIME"),
|
|
5
5
|
resolvedSubject: "Re: Quote",
|
|
6
6
|
threadedParentId: null,
|
|
7
|
+
draftMessageId: "<new-draft@x>",
|
|
7
8
|
attachments: [],
|
|
8
9
|
})),
|
|
9
|
-
replaceMock: vi.fn(async () => ({
|
|
10
|
+
replaceMock: vi.fn(async () => ({
|
|
11
|
+
folder: "Drafts",
|
|
12
|
+
oldUid: 5,
|
|
13
|
+
newUid: 99,
|
|
14
|
+
expunged: true,
|
|
15
|
+
created: false,
|
|
16
|
+
addressing: "uid-hit",
|
|
17
|
+
})),
|
|
10
18
|
}));
|
|
11
19
|
vi.mock("../lib/compose.js", () => ({ composeDraft: composeMock }));
|
|
12
20
|
vi.mock("../lib/imap.js", () => ({ replaceDraft: replaceMock }));
|
|
@@ -23,9 +31,17 @@ describe("Task 819 — email-draft-edit", () => {
|
|
|
23
31
|
raw: Buffer.from("MIME"),
|
|
24
32
|
resolvedSubject: "Re: Quote",
|
|
25
33
|
threadedParentId: null,
|
|
34
|
+
draftMessageId: "<new-draft@x>",
|
|
26
35
|
attachments: [],
|
|
27
36
|
});
|
|
28
|
-
replaceMock.mockResolvedValue({
|
|
37
|
+
replaceMock.mockResolvedValue({
|
|
38
|
+
folder: "Drafts",
|
|
39
|
+
oldUid: 5,
|
|
40
|
+
newUid: 99,
|
|
41
|
+
expunged: true,
|
|
42
|
+
created: false,
|
|
43
|
+
addressing: "uid-hit",
|
|
44
|
+
});
|
|
29
45
|
});
|
|
30
46
|
it("composes the replacement then replaces by UID; names the folder and both UIDs", async () => {
|
|
31
47
|
const text = await emailDraftEdit({
|
|
@@ -36,17 +52,59 @@ describe("Task 819 — email-draft-edit", () => {
|
|
|
36
52
|
body: "Revised body.",
|
|
37
53
|
});
|
|
38
54
|
expect(composeMock).toHaveBeenCalledTimes(1);
|
|
39
|
-
expect(replaceMock).toHaveBeenCalledWith(expect.anything(), "pw", 5, expect.any(Buffer));
|
|
55
|
+
expect(replaceMock).toHaveBeenCalledWith(expect.anything(), "pw", 5, expect.any(Buffer), null);
|
|
40
56
|
expect(text).toContain("Drafts");
|
|
41
57
|
expect(text).toContain("5");
|
|
42
58
|
expect(text).toContain("99");
|
|
43
59
|
expect(text).toMatch(/unsent/); // a revise never dispatches
|
|
44
60
|
});
|
|
61
|
+
it("passes the prior draft Message-ID to replaceDraft for relocation", async () => {
|
|
62
|
+
await emailDraftEdit({
|
|
63
|
+
accountId: "acc-1",
|
|
64
|
+
targetUid: 5,
|
|
65
|
+
to: "c@d.e",
|
|
66
|
+
subject: "Quote",
|
|
67
|
+
body: "v2",
|
|
68
|
+
draftMessageId: "<old-draft@x>",
|
|
69
|
+
});
|
|
70
|
+
expect(replaceMock).toHaveBeenCalledWith(expect.anything(), "pw", 5, expect.any(Buffer), "<old-draft@x>");
|
|
71
|
+
});
|
|
72
|
+
it("surfaces the new draft's Message-ID so the next edit can relocate it", async () => {
|
|
73
|
+
const text = await emailDraftEdit({
|
|
74
|
+
accountId: "acc-1",
|
|
75
|
+
targetUid: 5,
|
|
76
|
+
to: "c@d.e",
|
|
77
|
+
subject: "Quote",
|
|
78
|
+
body: "v2",
|
|
79
|
+
});
|
|
80
|
+
expect(text).toContain("draft id: <new-draft@x>");
|
|
81
|
+
});
|
|
82
|
+
it("reports the create-on-absent recovery when the prior draft was gone", async () => {
|
|
83
|
+
replaceMock.mockResolvedValueOnce({
|
|
84
|
+
folder: "Drafts",
|
|
85
|
+
oldUid: 5,
|
|
86
|
+
newUid: 77,
|
|
87
|
+
expunged: false,
|
|
88
|
+
created: true,
|
|
89
|
+
addressing: "created-fresh",
|
|
90
|
+
});
|
|
91
|
+
const text = await emailDraftEdit({
|
|
92
|
+
accountId: "acc-1",
|
|
93
|
+
targetUid: 5,
|
|
94
|
+
to: "c@d.e",
|
|
95
|
+
subject: "Quote",
|
|
96
|
+
body: "v2",
|
|
97
|
+
draftMessageId: "<old-draft@x>",
|
|
98
|
+
});
|
|
99
|
+
expect(text).toMatch(/no longer present/i);
|
|
100
|
+
expect(text).toContain("77");
|
|
101
|
+
});
|
|
45
102
|
it("names the threaded parent when the revision is a threaded draft", async () => {
|
|
46
103
|
composeMock.mockResolvedValueOnce({
|
|
47
104
|
raw: Buffer.from("MIME"),
|
|
48
105
|
resolvedSubject: "Re: Quote",
|
|
49
106
|
threadedParentId: "<parent@x>",
|
|
107
|
+
draftMessageId: "<new-draft@x>",
|
|
50
108
|
attachments: [],
|
|
51
109
|
});
|
|
52
110
|
const text = await emailDraftEdit({
|
|
@@ -63,9 +121,9 @@ describe("Task 819 — email-draft-edit", () => {
|
|
|
63
121
|
await expect(emailDraftEdit({ accountId: "acc-1", targetUid: 5, to: "c@d.e", body: "v2", messageId: "<gone@x>" })).rejects.toThrow(/No email found/);
|
|
64
122
|
expect(replaceMock).not.toHaveBeenCalled();
|
|
65
123
|
});
|
|
66
|
-
it("surfaces a
|
|
67
|
-
replaceMock.mockRejectedValueOnce(new Error("
|
|
68
|
-
await expect(emailDraftEdit({ accountId: "acc-1", targetUid: 404, to: "c@d.e", subject: "Q", body: "v2" })).rejects.toThrow(/
|
|
124
|
+
it("surfaces a persistent transient-drop error from replaceDraft verbatim", async () => {
|
|
125
|
+
replaceMock.mockRejectedValueOnce(new Error("Connection not available"));
|
|
126
|
+
await expect(emailDraftEdit({ accountId: "acc-1", targetUid: 404, to: "c@d.e", subject: "Q", body: "v2" })).rejects.toThrow(/Connection not available/);
|
|
69
127
|
});
|
|
70
128
|
});
|
|
71
129
|
//# sourceMappingURL=email-draft-edit.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email-draft-edit.test.js","sourceRoot":"","sources":["../../src/__tests__/email-draft-edit.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9D,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACrD,WAAW,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;QAC9B,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;QACxB,eAAe,EAAE,WAAW;QAC5B,gBAAgB,EAAE,IAAqB;QACvC,WAAW,EAAE,EAA+C;KAC7D,CAAC,CAAC;IACH,WAAW,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"email-draft-edit.test.js","sourceRoot":"","sources":["../../src/__tests__/email-draft-edit.test.ts"],"names":[],"mappings":"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"}
|