opensentinel 3.1.1 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +138 -83
- package/dist/agent-manager-JZ4IM7XI.js +39 -0
- package/dist/agent-processor-DDDHC2SO.js +281 -0
- package/dist/agent-processor-DDDHC2SO.js.map +1 -0
- package/dist/agent-types-2T4PXLFQ.js +12 -0
- package/dist/alerting-LK7VVYTX.js +699 -0
- package/dist/alerting-LK7VVYTX.js.map +1 -0
- package/dist/analysis-agent-JWN2GXYE.js +288 -0
- package/dist/analysis-agent-JWN2GXYE.js.map +1 -0
- package/dist/analyzer-OTWE3ARE.js +22 -0
- package/dist/{archiver-AVNBYCKQ.js → archiver-FPGKRP6P.js} +2 -2
- package/dist/{audit-logger-OBPR7CRO.js → audit-logger-CI4WZQPD.js} +6 -5
- package/dist/{auth-UOX5K2BE.js → auth-PH5IHISW.js} +2 -2
- package/dist/{autonomy-ZXDBDQUJ.js → autonomy-N7W5XPLX.js} +4 -3
- package/dist/autonomy-N7W5XPLX.js.map +1 -0
- package/dist/{aws-s3-Q4LLZZPD.js → aws-s3-QZMURYXB.js} +2 -2
- package/dist/{backup-restore-PZ7CYYB7.js → backup-restore-72OQTZO3.js} +2 -2
- package/dist/{blocks-R3PODY47.js → blocks-YOWOESDD.js} +4 -4
- package/dist/bot-VDHBGUVI.js +47 -0
- package/dist/brain-6QTXN4QP.js +66 -0
- package/dist/{camera-monitor-M5CYKUU4.js → camera-monitor-LHTUWHEL.js} +2 -2
- package/dist/{charts-V7ARZNKF.js → charts-FJ32GQK7.js} +2 -2
- package/dist/{chunk-WRAKK6K6.js → chunk-2I5QHYG6.js} +5 -3
- package/dist/chunk-2I5QHYG6.js.map +1 -0
- package/dist/{chunk-TVEWKIK3.js → chunk-2WTKTG2C.js} +2 -2
- package/dist/chunk-3AWAWRWB.js +143 -0
- package/dist/chunk-3AWAWRWB.js.map +1 -0
- package/dist/{chunk-ZLZKF2PM.js → chunk-4KIHDIXZ.js} +43 -2
- package/dist/chunk-4KIHDIXZ.js.map +1 -0
- package/dist/{chunk-EVE7MIIY.js → chunk-4WH6MFEW.js} +15 -16
- package/dist/chunk-4WH6MFEW.js.map +1 -0
- package/dist/{chunk-I6BDYQIG.js → chunk-56UJS2LA.js} +6 -6
- package/dist/chunk-56UJS2LA.js.map +1 -0
- package/dist/chunk-5BTVJR7R.js +37 -0
- package/dist/chunk-5BTVJR7R.js.map +1 -0
- package/dist/chunk-5JJTLWOR.js +1021 -0
- package/dist/chunk-5JJTLWOR.js.map +1 -0
- package/dist/chunk-66SAOZPU.js +236 -0
- package/dist/chunk-66SAOZPU.js.map +1 -0
- package/dist/chunk-6HGMRR4J.js +113 -0
- package/dist/chunk-6HGMRR4J.js.map +1 -0
- package/dist/chunk-6W6PTJFT.js +181 -0
- package/dist/chunk-6W6PTJFT.js.map +1 -0
- package/dist/chunk-6ZNCY2GI.js +418 -0
- package/dist/chunk-6ZNCY2GI.js.map +1 -0
- package/dist/chunk-7BNFELEK.js +31 -0
- package/dist/chunk-7BNFELEK.js.map +1 -0
- package/dist/chunk-ADTDYJO7.js +265 -0
- package/dist/chunk-ADTDYJO7.js.map +1 -0
- package/dist/{chunk-OCVQGBJK.js → chunk-BBN4VCNK.js} +6 -4
- package/dist/chunk-BBN4VCNK.js.map +1 -0
- package/dist/{chunk-SJSUSJ47.js → chunk-BNZHWAZC.js} +2 -2
- package/dist/chunk-C6PELIHS.js +60 -0
- package/dist/chunk-C6PELIHS.js.map +1 -0
- package/dist/{chunk-MQJ2ECQT.js → chunk-CUPEENUY.js} +3 -3
- package/dist/{chunk-NHMBTUMW.js → chunk-CWT6CAE5.js} +2 -2
- package/dist/{chunk-4GLYY4NN.js → chunk-CZTMGHUC.js} +8 -2
- package/dist/chunk-CZTMGHUC.js.map +1 -0
- package/dist/{chunk-RZ4YESBG.js → chunk-DOYGMNMK.js} +1 -1
- package/dist/chunk-DOYGMNMK.js.map +1 -0
- package/dist/chunk-DTISLIMB.js +89 -0
- package/dist/chunk-DTISLIMB.js.map +1 -0
- package/dist/{chunk-SPPMCAKG.js → chunk-GBVJTRXS.js} +2 -2
- package/dist/chunk-GBVJTRXS.js.map +1 -0
- package/dist/{chunk-AYUKPTSM.js → chunk-GJETKBOY.js} +96 -218
- package/dist/chunk-GJETKBOY.js.map +1 -0
- package/dist/{chunk-BXZ6EA52.js → chunk-GW6V4D43.js} +57 -3
- package/dist/chunk-GW6V4D43.js.map +1 -0
- package/dist/{chunk-6PMVAAA7.js → chunk-HJSEEFO3.js} +3 -3
- package/dist/{chunk-TYAGMJNV.js → chunk-HQZQFEAX.js} +5 -5
- package/dist/{chunk-MXAPLSJ5.js → chunk-J4JW73TT.js} +2 -2
- package/dist/{chunk-VEHFVBLI.js → chunk-JHYYFPKX.js} +2 -2
- package/dist/chunk-LFDXEYYB.js +150 -0
- package/dist/chunk-LFDXEYYB.js.map +1 -0
- package/dist/chunk-MIC5IBQF.js +386 -0
- package/dist/chunk-MIC5IBQF.js.map +1 -0
- package/dist/chunk-ODCFS5WD.js +463 -0
- package/dist/chunk-ODCFS5WD.js.map +1 -0
- package/dist/{chunk-XMCVRVTF.js → chunk-P64EV4YY.js} +1 -1
- package/dist/chunk-P64EV4YY.js.map +1 -0
- package/dist/chunk-PBOCSGNL.js +84 -0
- package/dist/chunk-PBOCSGNL.js.map +1 -0
- package/dist/{chunk-66OJ3WB4.js → chunk-PD3CTDO6.js} +2 -2
- package/dist/chunk-QPY3WRVM.js +647 -0
- package/dist/chunk-QPY3WRVM.js.map +1 -0
- package/dist/chunk-S2EOIVF4.js +3907 -0
- package/dist/chunk-S2EOIVF4.js.map +1 -0
- package/dist/chunk-SDLOMKCW.js +213 -0
- package/dist/chunk-SDLOMKCW.js.map +1 -0
- package/dist/chunk-TKBVW7ZJ.js +162 -0
- package/dist/chunk-TKBVW7ZJ.js.map +1 -0
- package/dist/{chunk-BRBWNV65.js → chunk-U2X2J3FI.js} +3 -3
- package/dist/chunk-U2X2J3FI.js.map +1 -0
- package/dist/{chunk-PLDDJCW6.js → chunk-UP2VWCW5.js} +1 -12
- package/dist/{chunk-SVAPX2XN.js → chunk-V3OKHQUX.js} +9 -7
- package/dist/{chunk-SVAPX2XN.js.map → chunk-V3OKHQUX.js.map} +1 -1
- package/dist/{chunk-4UOE5TUZ.js → chunk-WMDVOWN6.js} +4 -4
- package/dist/chunk-WMFYI7XC.js +564 -0
- package/dist/chunk-WMFYI7XC.js.map +1 -0
- package/dist/chunk-WZAH34TG.js +129 -0
- package/dist/chunk-WZAH34TG.js.map +1 -0
- package/dist/{chunk-H5RQOFO2.js → chunk-X6Q3K3L2.js} +6 -6
- package/dist/chunk-X6Q3K3L2.js.map +1 -0
- package/dist/chunk-XTX7EK43.js +134 -0
- package/dist/chunk-XTX7EK43.js.map +1 -0
- package/dist/chunk-YEDEAX6Y.js +194 -0
- package/dist/chunk-YEDEAX6Y.js.map +1 -0
- package/dist/{chunk-XKYRH4FM.js → chunk-ZIBRVA3Y.js} +70 -30
- package/dist/chunk-ZIBRVA3Y.js.map +1 -0
- package/dist/chunk-ZIYTHUM5.js +457 -0
- package/dist/chunk-ZIYTHUM5.js.map +1 -0
- package/dist/{chunk-766ASQWE.js → chunk-ZMML6T63.js} +2711 -2329
- package/dist/chunk-ZMML6T63.js.map +1 -0
- package/dist/chunk-ZVHG4KF2.js +380 -0
- package/dist/chunk-ZVHG4KF2.js.map +1 -0
- package/dist/chunker-K6WTR62A.js +12 -0
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/{client-ZQSFPMOB.js → client-FOIYPOZQ.js} +5 -6
- package/dist/{clipboard-manager-TEO2GEDN.js → clipboard-manager-4SBNESGZ.js} +2 -2
- package/dist/coding-agent-DESSU3AC.js +233 -0
- package/dist/coding-agent-DESSU3AC.js.map +1 -0
- package/dist/commands/setup.js +2 -2
- package/dist/commands/start.js +3 -3
- package/dist/commands/status.js +2 -2
- package/dist/commands/stop.js +2 -2
- package/dist/commands/utils.js +2 -2
- package/dist/cost-tracker-KZQSTSE2.js +11 -0
- package/dist/{cron-explain-HHQKPD3M.js → cron-explain-UOOOYWZZ.js} +2 -2
- package/dist/{crypto-4AP47IKC.js → crypto-2VG3RJR2.js} +2 -2
- package/dist/{databases-37X4CI2Y.js → databases-XDPMG5AV.js} +4 -4
- package/dist/db-I7MNG6CL.js +83 -0
- package/dist/discord-6UQHCN27.js +81 -0
- package/dist/documents-PFHSK7SZ.js +184 -0
- package/dist/documents-PFHSK7SZ.js.map +1 -0
- package/dist/docx-parser-EXL4TN5E.js +16 -0
- package/dist/{email-K7LO2IPB.js → email-6OIN4SYL.js} +34 -25
- package/dist/email-6OIN4SYL.js.map +1 -0
- package/dist/{enhanced-retrieval-DNLLEM4Z.js → enhanced-retrieval-JWX2HWU4.js} +12 -8
- package/dist/{enhanced-retrieval-DNLLEM4Z.js.map → enhanced-retrieval-JWX2HWU4.js.map} +1 -1
- package/dist/enrichment-pipeline-7FE5R5ZI.js +14 -0
- package/dist/{entity-resolution-Y3IUWEAT.js → entity-resolution-7Z6STVXX.js} +6 -5
- package/dist/env-GN5VHI43.js +12 -0
- package/dist/error-tracker-64DEH3D7.js +32 -0
- package/dist/finnhub-X7ZMQSXF.js +178 -0
- package/dist/finnhub-X7ZMQSXF.js.map +1 -0
- package/dist/fred-TMUF3J2V.js +203 -0
- package/dist/fred-TMUF3J2V.js.map +1 -0
- package/dist/github-DUWSXCNP.js +833 -0
- package/dist/github-DUWSXCNP.js.map +1 -0
- package/dist/{google-workspace-DKWUVNGC.js → google-workspace-TSZPZK5G.js} +2 -2
- package/dist/graph-client-NB475AK5.js +17 -0
- package/dist/{hash-tool-ULQYD7B5.js → hash-tool-ENAB5LWH.js} +2 -2
- package/dist/{heartbeat-monitor-GCISLXI3.js → heartbeat-monitor-KRDYTDBF.js} +2 -2
- package/dist/hooks-N4MIFBVM.js +14 -0
- package/dist/{image-generation-OSU7FP6F.js → image-generation-MDE6AVQO.js} +2 -2
- package/dist/imessage-DSGSGUZS.js +44 -0
- package/dist/inbox-summarizer-F2KAU72V.js +56 -0
- package/dist/{incident-response-C5J7Q6DT.js → incident-response-E3UGMX5G.js} +8 -6
- package/dist/incident-response-E3UGMX5G.js.map +1 -0
- package/dist/{inventory-manager-352OHXWD.js → inventory-manager-C67BSZM6.js} +2 -2
- package/dist/{jira-GSGDBMIG.js → jira-PAGZWUBJ.js} +2 -2
- package/dist/{json-tool-QE2SYHEG.js → json-tool-4FK5RNER.js} +2 -2
- package/dist/{key-rotation-DPHU4ZTB.js → key-rotation-WCC5FOYS.js} +2 -2
- package/dist/knowledge-base-5SMMOGQJ.js +46 -0
- package/dist/lib.d.ts +94 -1
- package/dist/lib.js +83 -66
- package/dist/lib.js.map +1 -1
- package/dist/{mailchimp-KKNF6QJ7.js → mailchimp-ZFYDC44J.js} +2 -2
- package/dist/{matrix-QVHG76I7.js → matrix-WYGEOZL5.js} +30 -21
- package/dist/{matrix-QVHG76I7.js.map → matrix-WYGEOZL5.js.map} +1 -1
- package/dist/{mcp-3JI6W7ZE.js → mcp-DJ2QDA6A.js} +3 -3
- package/dist/metrics-BH3ZLGEV.js +25 -0
- package/dist/{microsoft365-UCBKJHNX.js → microsoft365-6G2IJMWC.js} +2 -2
- package/dist/multi-user-XAEMB244.js +411 -0
- package/dist/multi-user-XAEMB244.js.map +1 -0
- package/dist/oauth-UPJYFOVU.js +34 -0
- package/dist/{ocr-AC7NPX33.js → ocr-UONKTQU7.js} +6 -4
- package/dist/{ollama-BOAMSPLJ.js → ollama-J7CU45WT.js} +2 -2
- package/dist/osint-agent-RL5XPBRQ.js +189 -0
- package/dist/osint-agent-RL5XPBRQ.js.map +1 -0
- package/dist/{pages-MI523RB7.js → pages-XDE7JRCA.js} +5 -5
- package/dist/{pair-JDFTERIK.js → pair-YZJFQUU5.js} +2 -2
- package/dist/{pairing-IFQYCPNS.js → pairing-77N47RAT.js} +2 -2
- package/dist/{pdf-ALQVOEJR.js → pdf-67HGXCFJ.js} +3 -3
- package/dist/pdf-parser-YLMTTYHL.js +14 -0
- package/dist/{presentations-DSV5IHG5.js → presentations-UOET2FVZ.js} +3 -3
- package/dist/presentations-UOET2FVZ.js.map +1 -0
- package/dist/{prometheus-JNT2BD4L.js → prometheus-YETCZO4I.js} +2 -2
- package/dist/prometheus-YETCZO4I.js.map +1 -0
- package/dist/{providers-J4LYPHDR.js → providers-2YQ6E3IF.js} +6 -4
- package/dist/providers-2YQ6E3IF.js.map +1 -0
- package/dist/{qr-code-WIX4PB4U.js → qr-code-6WZJHRKL.js} +2 -2
- package/dist/qr-code-6WZJHRKL.js.map +1 -0
- package/dist/{quickbooks-XB4NII2S.js → quickbooks-N675W7IK.js} +2 -2
- package/dist/{regex-tool-W4ABRKGK.js → regex-tool-6Q63LQ7B.js} +2 -2
- package/dist/regex-tool-6Q63LQ7B.js.map +1 -0
- package/dist/research-agent-WCRSY3UZ.js +168 -0
- package/dist/research-agent-WCRSY3UZ.js.map +1 -0
- package/dist/risk-engine-YKCPT5D5.js +10 -0
- package/dist/risk-engine-YKCPT5D5.js.map +1 -0
- package/dist/scheduler-6PLLAQI7.js +74 -0
- package/dist/scheduler-6PLLAQI7.js.map +1 -0
- package/dist/schema-ETY7L2VA.js +78 -0
- package/dist/schema-ETY7L2VA.js.map +1 -0
- package/dist/{search-BCLBO5E3.js → search-GMLKBHSW.js} +4 -4
- package/dist/search-GMLKBHSW.js.map +1 -0
- package/dist/{sendgrid-RNXCAFKM.js → sendgrid-QGJIVPWV.js} +2 -2
- package/dist/sharepoint-V5P4Q62L.js +30 -0
- package/dist/sharepoint-V5P4Q62L.js.map +1 -0
- package/dist/{shopify-NCXYJB4R.js → shopify-ON2PAU27.js} +2 -2
- package/dist/signal-7D5EPGVL.js +44 -0
- package/dist/signal-7D5EPGVL.js.map +1 -0
- package/dist/slack-KSS6YK5Z.js +86 -0
- package/dist/slack-KSS6YK5Z.js.map +1 -0
- package/dist/{sms-M3JIOTCW.js → sms-CSUCC7HL.js} +4 -4
- package/dist/sms-CSUCC7HL.js.map +1 -0
- package/dist/{src-VYUE6LRA.js → src-GO7GGW7O.js} +190 -52
- package/dist/src-GO7GGW7O.js.map +1 -0
- package/dist/{stocks-XXWBPOCU.js → stocks-4M4HZWZS.js} +2 -2
- package/dist/stocks-4M4HZWZS.js.map +1 -0
- package/dist/text-extractor-OAUBAW5P.js +12 -0
- package/dist/text-extractor-OAUBAW5P.js.map +1 -0
- package/dist/{text-transform-6SGUA5Z4.js → text-transform-HCLCUDFZ.js} +2 -2
- package/dist/text-transform-HCLCUDFZ.js.map +1 -0
- package/dist/token-store-SEWRX6RE.js +20 -0
- package/dist/token-store-SEWRX6RE.js.map +1 -0
- package/dist/tools-PJZ6RI4P.js +47 -0
- package/dist/tools-PJZ6RI4P.js.map +1 -0
- package/dist/{tunnel-IWMXUML4.js → tunnel-XOUVVRAK.js} +4 -2
- package/dist/tunnel-XOUVVRAK.js.map +1 -0
- package/dist/{twilio-53GEW5JT.js → twilio-3L7DUNYQ.js} +2 -2
- package/dist/{unit-converter-ZYXMEZOE.js → unit-converter-LYPAHU64.js} +2 -2
- package/dist/unit-converter-LYPAHU64.js.map +1 -0
- package/dist/whatsapp-DWXK25V2.js +44 -0
- package/dist/whatsapp-DWXK25V2.js.map +1 -0
- package/dist/{word-document-7B6SJMAY.js → word-document-AV3YB4L2.js} +4 -4
- package/dist/word-document-AV3YB4L2.js.map +1 -0
- package/dist/workflow-store-5Y56GUP7.js +373 -0
- package/dist/workflow-store-5Y56GUP7.js.map +1 -0
- package/dist/writing-agent-VDGLNOGO.js +243 -0
- package/dist/writing-agent-VDGLNOGO.js.map +1 -0
- package/dist/{xero-QYO66D45.js → xero-UHAHVYSD.js} +2 -2
- package/dist/{zapier-webhook-TBZ5YF2A.js → zapier-webhook-NIELLTXR.js} +2 -2
- package/drizzle/0002_mushy_master_mold.sql +139 -139
- package/drizzle/0003_overjoyed_rhodey.sql +46 -0
- package/drizzle/meta/0002_snapshot.json +3636 -3636
- package/drizzle/meta/0003_snapshot.json +3946 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +110 -100
- package/dist/autonomy-ZXDBDQUJ.js.map +0 -1
- package/dist/bot-QRARP4UN.js +0 -36
- package/dist/brain-7XLLM3KC.js +0 -56
- package/dist/chunk-4GLYY4NN.js.map +0 -1
- package/dist/chunk-766ASQWE.js.map +0 -1
- package/dist/chunk-AYUKPTSM.js.map +0 -1
- package/dist/chunk-BRBWNV65.js.map +0 -1
- package/dist/chunk-BXZ6EA52.js.map +0 -1
- package/dist/chunk-EVE7MIIY.js.map +0 -1
- package/dist/chunk-H5RQOFO2.js.map +0 -1
- package/dist/chunk-I6BDYQIG.js.map +0 -1
- package/dist/chunk-IZJMVV7O.js +0 -347
- package/dist/chunk-IZJMVV7O.js.map +0 -1
- package/dist/chunk-O7IH7JTI.js +0 -1898
- package/dist/chunk-O7IH7JTI.js.map +0 -1
- package/dist/chunk-OCVQGBJK.js.map +0 -1
- package/dist/chunk-RZ4YESBG.js.map +0 -1
- package/dist/chunk-SPPMCAKG.js.map +0 -1
- package/dist/chunk-VRD5CYRL.js +0 -1568
- package/dist/chunk-VRD5CYRL.js.map +0 -1
- package/dist/chunk-WRAKK6K6.js.map +0 -1
- package/dist/chunk-XKYRH4FM.js.map +0 -1
- package/dist/chunk-XMCVRVTF.js.map +0 -1
- package/dist/chunk-ZLZKF2PM.js.map +0 -1
- package/dist/discord-B3HUPGQ6.js +0 -70
- package/dist/dist-UISMLMFN.js +0 -21847
- package/dist/dist-UISMLMFN.js.map +0 -1
- package/dist/email-K7LO2IPB.js.map +0 -1
- package/dist/enrichment-pipeline-MNHNW65K.js +0 -13
- package/dist/env-IWXUVTCB.js +0 -12
- package/dist/imessage-NGA2XF2V.js +0 -35
- package/dist/inbox-summarizer-NRI4S7IF.js +0 -47
- package/dist/incident-response-C5J7Q6DT.js.map +0 -1
- package/dist/presentations-DSV5IHG5.js.map +0 -1
- package/dist/scheduler-VK4WFERV.js +0 -63
- package/dist/signal-6CGDFYL2.js +0 -35
- package/dist/slack-IZQWIKOH.js +0 -75
- package/dist/src-VYUE6LRA.js.map +0 -1
- package/dist/tools-2RLEI2N6.js +0 -38
- package/dist/tunnel-IWMXUML4.js.map +0 -1
- package/dist/whatsapp-LFX6YKCM.js +0 -35
- package/dist/word-document-7B6SJMAY.js.map +0 -1
- /package/dist/{audit-logger-OBPR7CRO.js.map → agent-manager-JZ4IM7XI.js.map} +0 -0
- /package/dist/{auth-UOX5K2BE.js.map → agent-types-2T4PXLFQ.js.map} +0 -0
- /package/dist/{backup-restore-PZ7CYYB7.js.map → analyzer-OTWE3ARE.js.map} +0 -0
- /package/dist/{archiver-AVNBYCKQ.js.map → archiver-FPGKRP6P.js.map} +0 -0
- /package/dist/{blocks-R3PODY47.js.map → audit-logger-CI4WZQPD.js.map} +0 -0
- /package/dist/{bot-QRARP4UN.js.map → auth-PH5IHISW.js.map} +0 -0
- /package/dist/{aws-s3-Q4LLZZPD.js.map → aws-s3-QZMURYXB.js.map} +0 -0
- /package/dist/{brain-7XLLM3KC.js.map → backup-restore-72OQTZO3.js.map} +0 -0
- /package/dist/{chunk-PLDDJCW6.js.map → blocks-YOWOESDD.js.map} +0 -0
- /package/dist/{client-ZQSFPMOB.js.map → bot-VDHBGUVI.js.map} +0 -0
- /package/dist/{clipboard-manager-TEO2GEDN.js.map → brain-6QTXN4QP.js.map} +0 -0
- /package/dist/{camera-monitor-M5CYKUU4.js.map → camera-monitor-LHTUWHEL.js.map} +0 -0
- /package/dist/{charts-V7ARZNKF.js.map → charts-FJ32GQK7.js.map} +0 -0
- /package/dist/{chunk-TVEWKIK3.js.map → chunk-2WTKTG2C.js.map} +0 -0
- /package/dist/{chunk-SJSUSJ47.js.map → chunk-BNZHWAZC.js.map} +0 -0
- /package/dist/{chunk-MQJ2ECQT.js.map → chunk-CUPEENUY.js.map} +0 -0
- /package/dist/{chunk-NHMBTUMW.js.map → chunk-CWT6CAE5.js.map} +0 -0
- /package/dist/{chunk-6PMVAAA7.js.map → chunk-HJSEEFO3.js.map} +0 -0
- /package/dist/{chunk-TYAGMJNV.js.map → chunk-HQZQFEAX.js.map} +0 -0
- /package/dist/{chunk-MXAPLSJ5.js.map → chunk-J4JW73TT.js.map} +0 -0
- /package/dist/{chunk-VEHFVBLI.js.map → chunk-JHYYFPKX.js.map} +0 -0
- /package/dist/{chunk-66OJ3WB4.js.map → chunk-PD3CTDO6.js.map} +0 -0
- /package/dist/{cron-explain-HHQKPD3M.js.map → chunk-UP2VWCW5.js.map} +0 -0
- /package/dist/{chunk-4UOE5TUZ.js.map → chunk-WMDVOWN6.js.map} +0 -0
- /package/dist/{crypto-4AP47IKC.js.map → chunker-K6WTR62A.js.map} +0 -0
- /package/dist/{databases-37X4CI2Y.js.map → client-FOIYPOZQ.js.map} +0 -0
- /package/dist/{discord-B3HUPGQ6.js.map → clipboard-manager-4SBNESGZ.js.map} +0 -0
- /package/dist/{enrichment-pipeline-MNHNW65K.js.map → cost-tracker-KZQSTSE2.js.map} +0 -0
- /package/dist/{entity-resolution-Y3IUWEAT.js.map → cron-explain-UOOOYWZZ.js.map} +0 -0
- /package/dist/{env-IWXUVTCB.js.map → crypto-2VG3RJR2.js.map} +0 -0
- /package/dist/{hash-tool-ULQYD7B5.js.map → databases-XDPMG5AV.js.map} +0 -0
- /package/dist/{heartbeat-monitor-GCISLXI3.js.map → db-I7MNG6CL.js.map} +0 -0
- /package/dist/{imessage-NGA2XF2V.js.map → discord-6UQHCN27.js.map} +0 -0
- /package/dist/{inbox-summarizer-NRI4S7IF.js.map → docx-parser-EXL4TN5E.js.map} +0 -0
- /package/dist/{inventory-manager-352OHXWD.js.map → enrichment-pipeline-7FE5R5ZI.js.map} +0 -0
- /package/dist/{json-tool-QE2SYHEG.js.map → entity-resolution-7Z6STVXX.js.map} +0 -0
- /package/dist/{key-rotation-DPHU4ZTB.js.map → env-GN5VHI43.js.map} +0 -0
- /package/dist/{mcp-3JI6W7ZE.js.map → error-tracker-64DEH3D7.js.map} +0 -0
- /package/dist/{google-workspace-DKWUVNGC.js.map → google-workspace-TSZPZK5G.js.map} +0 -0
- /package/dist/{ocr-AC7NPX33.js.map → graph-client-NB475AK5.js.map} +0 -0
- /package/dist/{ollama-BOAMSPLJ.js.map → hash-tool-ENAB5LWH.js.map} +0 -0
- /package/dist/{pages-MI523RB7.js.map → heartbeat-monitor-KRDYTDBF.js.map} +0 -0
- /package/dist/{pairing-IFQYCPNS.js.map → hooks-N4MIFBVM.js.map} +0 -0
- /package/dist/{image-generation-OSU7FP6F.js.map → image-generation-MDE6AVQO.js.map} +0 -0
- /package/dist/{pdf-ALQVOEJR.js.map → imessage-DSGSGUZS.js.map} +0 -0
- /package/dist/{prometheus-JNT2BD4L.js.map → inbox-summarizer-F2KAU72V.js.map} +0 -0
- /package/dist/{providers-J4LYPHDR.js.map → inventory-manager-C67BSZM6.js.map} +0 -0
- /package/dist/{jira-GSGDBMIG.js.map → jira-PAGZWUBJ.js.map} +0 -0
- /package/dist/{qr-code-WIX4PB4U.js.map → json-tool-4FK5RNER.js.map} +0 -0
- /package/dist/{regex-tool-W4ABRKGK.js.map → key-rotation-WCC5FOYS.js.map} +0 -0
- /package/dist/{scheduler-VK4WFERV.js.map → knowledge-base-5SMMOGQJ.js.map} +0 -0
- /package/dist/{mailchimp-KKNF6QJ7.js.map → mailchimp-ZFYDC44J.js.map} +0 -0
- /package/dist/{search-BCLBO5E3.js.map → mcp-DJ2QDA6A.js.map} +0 -0
- /package/dist/{signal-6CGDFYL2.js.map → metrics-BH3ZLGEV.js.map} +0 -0
- /package/dist/{microsoft365-UCBKJHNX.js.map → microsoft365-6G2IJMWC.js.map} +0 -0
- /package/dist/{slack-IZQWIKOH.js.map → oauth-UPJYFOVU.js.map} +0 -0
- /package/dist/{sms-M3JIOTCW.js.map → ocr-UONKTQU7.js.map} +0 -0
- /package/dist/{stocks-XXWBPOCU.js.map → ollama-J7CU45WT.js.map} +0 -0
- /package/dist/{text-transform-6SGUA5Z4.js.map → pages-XDE7JRCA.js.map} +0 -0
- /package/dist/{pair-JDFTERIK.js.map → pair-YZJFQUU5.js.map} +0 -0
- /package/dist/{tools-2RLEI2N6.js.map → pairing-77N47RAT.js.map} +0 -0
- /package/dist/{unit-converter-ZYXMEZOE.js.map → pdf-67HGXCFJ.js.map} +0 -0
- /package/dist/{whatsapp-LFX6YKCM.js.map → pdf-parser-YLMTTYHL.js.map} +0 -0
- /package/dist/{quickbooks-XB4NII2S.js.map → quickbooks-N675W7IK.js.map} +0 -0
- /package/dist/{sendgrid-RNXCAFKM.js.map → sendgrid-QGJIVPWV.js.map} +0 -0
- /package/dist/{shopify-NCXYJB4R.js.map → shopify-ON2PAU27.js.map} +0 -0
- /package/dist/{twilio-53GEW5JT.js.map → twilio-3L7DUNYQ.js.map} +0 -0
- /package/dist/{xero-QYO66D45.js.map → xero-UHAHVYSD.js.map} +0 -0
- /package/dist/{zapier-webhook-TBZ5YF2A.js.map → zapier-webhook-NIELLTXR.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/mcp/client.ts","../src/core/mcp/registry.ts","../src/core/mcp/tool-bridge.ts"],"sourcesContent":["/**\r\n * MCP Client - Handles communication with MCP servers\r\n * Supports STDIO and HTTP+SSE transports\r\n */\r\n\r\nimport { spawn, type ChildProcess } from \"node:child_process\";\r\nimport { nanoid } from \"nanoid\";\r\nimport type {\r\n MCPServerConfig,\r\n MCPServerState,\r\n MCPTool,\r\n MCPInitializeParams,\r\n MCPInitializeResult,\r\n MCPToolListResult,\r\n MCPToolCallParams,\r\n MCPToolCallResult,\r\n MCPToolResult,\r\n JsonRpcRequest,\r\n JsonRpcResponse,\r\n} from \"./types\";\r\n\r\nconst MCP_PROTOCOL_VERSION = \"2024-11-05\";\r\n\r\nexport class MCPClient {\r\n private config: MCPServerConfig;\r\n private state: MCPServerState;\r\n private process: ChildProcess | null = null;\r\n private pendingRequests: Map<string | number, {\r\n resolve: (value: unknown) => void;\r\n reject: (error: Error) => void;\r\n timeout: Timer;\r\n }> = new Map();\r\n private messageBuffer = \"\";\r\n private requestTimeout: number;\r\n\r\n constructor(config: MCPServerConfig, timeout = 30000) {\r\n this.config = config;\r\n this.requestTimeout = timeout;\r\n this.state = {\r\n config,\r\n status: \"disconnected\",\r\n tools: [],\r\n };\r\n }\r\n\r\n get id(): string {\r\n return this.config.id;\r\n }\r\n\r\n get name(): string {\r\n return this.config.name;\r\n }\r\n\r\n get status(): MCPServerState[\"status\"] {\r\n return this.state.status;\r\n }\r\n\r\n get tools(): MCPTool[] {\r\n return this.state.tools;\r\n }\r\n\r\n get serverInfo() {\r\n return this.state.serverInfo;\r\n }\r\n\r\n // ============================================\r\n // CONNECTION MANAGEMENT\r\n // ============================================\r\n\r\n async connect(): Promise<void> {\r\n if (this.state.status === \"connected\") {\r\n return;\r\n }\r\n\r\n this.state.status = \"connecting\";\r\n\r\n try {\r\n if (this.config.transport === \"stdio\") {\r\n await this.connectStdio();\r\n } else if (this.config.transport === \"http+sse\") {\r\n await this.connectHttpSse();\r\n } else {\r\n throw new Error(`Unsupported transport: ${this.config.transport}`);\r\n }\r\n\r\n // Initialize MCP connection\r\n const initResult = await this.initialize();\r\n this.state.capabilities = initResult.capabilities;\r\n this.state.serverInfo = initResult.serverInfo;\r\n\r\n // Fetch available tools\r\n await this.refreshTools();\r\n\r\n this.state.status = \"connected\";\r\n this.state.lastActivity = new Date();\r\n\r\n console.log(`[MCP] Connected to ${this.name} (${this.state.tools.length} tools)`);\r\n } catch (error) {\r\n this.state.status = \"error\";\r\n this.state.lastError = error instanceof Error ? error.message : \"Unknown error\";\r\n throw error;\r\n }\r\n }\r\n\r\n async disconnect(): Promise<void> {\r\n if (this.state.status === \"disconnected\") {\r\n return;\r\n }\r\n\r\n // Cancel pending requests\r\n for (const [, pending] of this.pendingRequests) {\r\n clearTimeout(pending.timeout);\r\n pending.reject(new Error(\"Connection closed\"));\r\n }\r\n this.pendingRequests.clear();\r\n\r\n // Kill subprocess if STDIO\r\n if (this.process) {\r\n this.process.kill();\r\n this.process = null;\r\n }\r\n\r\n this.state.status = \"disconnected\";\r\n this.state.tools = [];\r\n console.log(`[MCP] Disconnected from ${this.name}`);\r\n }\r\n\r\n // ============================================\r\n // STDIO TRANSPORT\r\n // ============================================\r\n\r\n private async connectStdio(): Promise<void> {\r\n if (!this.config.command) {\r\n throw new Error(\"STDIO transport requires a command\");\r\n }\r\n\r\n const env: Record<string, string> = {\r\n ...process.env as Record<string, string>,\r\n ...this.config.env,\r\n };\r\n\r\n this.process = spawn(\r\n this.config.command,\r\n this.config.args || [],\r\n {\r\n cwd: this.config.cwd || process.cwd(),\r\n env,\r\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\r\n }\r\n );\r\n\r\n // Start reading stdout in background\r\n this.readStdout();\r\n\r\n // Start reading stderr in background\r\n this.readStderr();\r\n\r\n // Give the process a moment to start\r\n await new Promise((resolve) => setTimeout(resolve, 500));\r\n }\r\n\r\n private readStdout(): void {\r\n if (!this.process?.stdout) return;\r\n\r\n this.process.stdout.on(\"data\", (chunk: Buffer) => {\r\n this.messageBuffer += chunk.toString();\r\n this.processMessageBuffer();\r\n });\r\n\r\n this.process.stdout.on(\"error\", (error) => {\r\n if (this.state.status === \"connected\" || this.state.status === \"connecting\") {\r\n console.error(`[MCP] ${this.name} stdout error:`, error);\r\n }\r\n });\r\n }\r\n\r\n private readStderr(): void {\r\n if (!this.process?.stderr) return;\r\n\r\n this.process.stderr.on(\"data\", (chunk: Buffer) => {\r\n const text = chunk.toString();\r\n if (text.trim()) {\r\n console.log(`[MCP] ${this.name} stderr: ${text.trim()}`);\r\n }\r\n });\r\n }\r\n\r\n private processMessageBuffer(): void {\r\n // JSON-RPC messages are newline-delimited\r\n const lines = this.messageBuffer.split(\"\\n\");\r\n this.messageBuffer = lines.pop() || \"\";\r\n\r\n for (const line of lines) {\r\n if (!line.trim()) continue;\r\n\r\n try {\r\n const message = JSON.parse(line) as JsonRpcResponse;\r\n this.handleResponse(message);\r\n } catch {\r\n console.warn(`[MCP] ${this.name} failed to parse message: ${line.slice(0, 100)}`);\r\n }\r\n }\r\n }\r\n\r\n private handleResponse(response: JsonRpcResponse): void {\r\n if (response.id === undefined || response.id === null) {\r\n // Notification from server, ignore for now\r\n return;\r\n }\r\n\r\n const pending = this.pendingRequests.get(response.id);\r\n if (!pending) {\r\n console.warn(`[MCP] ${this.name} received response for unknown request: ${response.id}`);\r\n return;\r\n }\r\n\r\n clearTimeout(pending.timeout);\r\n this.pendingRequests.delete(response.id);\r\n\r\n if (response.error) {\r\n pending.reject(new Error(response.error.message));\r\n } else {\r\n pending.resolve(response.result);\r\n }\r\n }\r\n\r\n private async sendRequest(method: string, params?: unknown): Promise<unknown> {\r\n if (!this.process?.stdin) {\r\n throw new Error(\"Not connected\");\r\n }\r\n\r\n const id = nanoid();\r\n const request: JsonRpcRequest = {\r\n jsonrpc: \"2.0\",\r\n id,\r\n method,\r\n params,\r\n };\r\n\r\n const promise = new Promise((resolve, reject) => {\r\n const timeout = setTimeout(() => {\r\n this.pendingRequests.delete(id);\r\n reject(new Error(`Request timeout: ${method}`));\r\n }, this.requestTimeout);\r\n\r\n this.pendingRequests.set(id, { resolve, reject, timeout });\r\n });\r\n\r\n // Write request to stdin\r\n const data = JSON.stringify(request) + \"\\n\";\r\n this.process.stdin.write(data);\r\n\r\n return promise;\r\n }\r\n\r\n // ============================================\r\n // HTTP+SSE TRANSPORT\r\n // ============================================\r\n\r\n private async connectHttpSse(): Promise<void> {\r\n if (!this.config.url) {\r\n throw new Error(\"HTTP+SSE transport requires a URL\");\r\n }\r\n\r\n // For HTTP+SSE, we don't maintain a persistent connection\r\n // Each request is a separate HTTP call\r\n // SSE is used for streaming responses (not implemented yet)\r\n\r\n // Just verify the server is reachable\r\n try {\r\n const response = await fetch(`${this.config.url}/health`, {\r\n headers: this.config.headers,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new Error(`Server health check failed: ${response.status}`);\r\n }\r\n } catch (error) {\r\n // Health endpoint might not exist, try main endpoint\r\n const response = await fetch(this.config.url, {\r\n method: \"POST\",\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n ...this.config.headers,\r\n },\r\n body: JSON.stringify({\r\n jsonrpc: \"2.0\",\r\n id: \"ping\",\r\n method: \"ping\",\r\n }),\r\n });\r\n\r\n if (!response.ok) {\r\n throw new Error(`Server connection failed: ${response.status}`);\r\n }\r\n }\r\n }\r\n\r\n private async sendHttpRequest(method: string, params?: unknown): Promise<unknown> {\r\n if (!this.config.url) {\r\n throw new Error(\"HTTP+SSE transport requires a URL\");\r\n }\r\n\r\n const request: JsonRpcRequest = {\r\n jsonrpc: \"2.0\",\r\n id: nanoid(),\r\n method,\r\n params,\r\n };\r\n\r\n const response = await fetch(this.config.url, {\r\n method: \"POST\",\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n ...this.config.headers,\r\n },\r\n body: JSON.stringify(request),\r\n });\r\n\r\n if (!response.ok) {\r\n throw new Error(`HTTP request failed: ${response.status}`);\r\n }\r\n\r\n const result = await response.json() as JsonRpcResponse;\r\n\r\n if (result.error) {\r\n throw new Error(result.error.message);\r\n }\r\n\r\n return result.result;\r\n }\r\n\r\n // ============================================\r\n // MCP PROTOCOL METHODS\r\n // ============================================\r\n\r\n private async initialize(): Promise<MCPInitializeResult> {\r\n const params: MCPInitializeParams = {\r\n protocolVersion: MCP_PROTOCOL_VERSION,\r\n capabilities: {\r\n roots: { listChanged: true },\r\n },\r\n clientInfo: {\r\n name: \"OpenSentinel\",\r\n version: \"3.0.0\",\r\n },\r\n };\r\n\r\n const result = await this.request(\"initialize\", params) as MCPInitializeResult;\r\n\r\n // Send initialized notification\r\n await this.notify(\"notifications/initialized\", {});\r\n\r\n return result;\r\n }\r\n\r\n async refreshTools(): Promise<MCPTool[]> {\r\n const result = await this.request(\"tools/list\", {}) as MCPToolListResult;\r\n this.state.tools = result.tools || [];\r\n return this.state.tools;\r\n }\r\n\r\n async callTool(name: string, args?: Record<string, unknown>): Promise<MCPToolResult> {\r\n const params: MCPToolCallParams = {\r\n name,\r\n arguments: args,\r\n };\r\n\r\n try {\r\n const result = await this.request(\"tools/call\", params) as MCPToolCallResult;\r\n this.state.lastActivity = new Date();\r\n\r\n // Extract text content from result\r\n const textContent = result.content\r\n .filter((c) => c.type === \"text\" && c.text)\r\n .map((c) => c.text)\r\n .join(\"\\n\");\r\n\r\n return {\r\n success: !result.isError,\r\n output: textContent || JSON.stringify(result.content),\r\n isError: result.isError,\r\n };\r\n } catch (error) {\r\n return {\r\n success: false,\r\n error: error instanceof Error ? error.message : \"Unknown error\",\r\n };\r\n }\r\n }\r\n\r\n // ============================================\r\n // GENERIC REQUEST/NOTIFY\r\n // ============================================\r\n\r\n private async request(method: string, params?: unknown): Promise<unknown> {\r\n if (this.config.transport === \"stdio\") {\r\n return this.sendRequest(method, params);\r\n } else {\r\n return this.sendHttpRequest(method, params);\r\n }\r\n }\r\n\r\n private async notify(method: string, params?: unknown): Promise<void> {\r\n if (this.config.transport === \"stdio\" && this.process?.stdin) {\r\n const notification = {\r\n jsonrpc: \"2.0\",\r\n method,\r\n params,\r\n };\r\n this.process.stdin.write(JSON.stringify(notification) + \"\\n\");\r\n }\r\n // HTTP+SSE notifications are not typically supported\r\n }\r\n\r\n // ============================================\r\n // STATE ACCESS\r\n // ============================================\r\n\r\n getState(): MCPServerState {\r\n return { ...this.state };\r\n }\r\n}\r\n","/**\r\n * MCP Registry - Manages multiple MCP server connections\r\n * Aggregates tools from all connected servers\r\n */\r\n\r\nimport { MCPClient } from \"./client\";\r\nimport type {\r\n MCPConfig,\r\n MCPServerConfig,\r\n MCPServerState,\r\n MCPTool,\r\n MCPToolResult,\r\n} from \"./types\";\r\n\r\nexport class MCPRegistry {\r\n private clients: Map<string, MCPClient> = new Map();\r\n private config: MCPConfig;\r\n private defaultTimeout: number;\r\n\r\n constructor(config: MCPConfig) {\r\n this.config = config;\r\n this.defaultTimeout = config.settings?.timeout || 30000;\r\n }\r\n\r\n // ============================================\r\n // INITIALIZATION\r\n // ============================================\r\n\r\n /**\r\n * Connect to all enabled MCP servers\r\n */\r\n async initialize(): Promise<void> {\r\n const enabledServers = this.config.servers.filter((s) => s.enabled);\r\n\r\n console.log(`[MCP] Initializing ${enabledServers.length} server(s)...`);\r\n\r\n const withTimeout = (promise: Promise<void>, name: string, ms = 15000) =>\r\n Promise.race([\r\n promise,\r\n new Promise<void>((_, reject) =>\r\n setTimeout(() => reject(new Error(`${name} timed out after ${ms / 1000}s`)), ms)\r\n ),\r\n ]);\r\n\r\n const results = await Promise.allSettled(\r\n enabledServers.map((config) => withTimeout(this.connectServer(config), config.name))\r\n );\r\n\r\n // Log any failures\r\n results.forEach((result, index) => {\r\n if (result.status === \"rejected\") {\r\n console.error(\r\n `[MCP] Failed to connect to ${enabledServers[index].name}:`,\r\n result.reason\r\n );\r\n }\r\n });\r\n\r\n const connected = results.filter((r) => r.status === \"fulfilled\").length;\r\n console.log(`[MCP] Connected to ${connected}/${enabledServers.length} servers`);\r\n }\r\n\r\n /**\r\n * Connect to a single MCP server\r\n */\r\n async connectServer(config: MCPServerConfig): Promise<void> {\r\n if (this.clients.has(config.id)) {\r\n throw new Error(`Server ${config.id} already connected`);\r\n }\r\n\r\n const client = new MCPClient(config, this.defaultTimeout);\r\n await client.connect();\r\n this.clients.set(config.id, client);\r\n }\r\n\r\n /**\r\n * Disconnect from a specific server\r\n */\r\n async disconnectServer(id: string): Promise<void> {\r\n const client = this.clients.get(id);\r\n if (client) {\r\n await client.disconnect();\r\n this.clients.delete(id);\r\n }\r\n }\r\n\r\n /**\r\n * Disconnect from all servers\r\n */\r\n async shutdown(): Promise<void> {\r\n console.log(\"[MCP] Shutting down all connections...\");\r\n await Promise.all(\r\n Array.from(this.clients.values()).map((client) => client.disconnect())\r\n );\r\n this.clients.clear();\r\n }\r\n\r\n // ============================================\r\n // TOOL MANAGEMENT\r\n // ============================================\r\n\r\n /**\r\n * Get all tools from all connected servers\r\n * Tools are prefixed with \"mcp_{serverId}_\" for routing\r\n */\r\n getAllTools(): Array<{ serverId: string; tool: MCPTool }> {\r\n const tools: Array<{ serverId: string; tool: MCPTool }> = [];\r\n\r\n for (const [serverId, client] of this.clients) {\r\n if (client.status === \"connected\") {\r\n for (const tool of client.tools) {\r\n tools.push({ serverId, tool });\r\n }\r\n }\r\n }\r\n\r\n return tools;\r\n }\r\n\r\n /**\r\n * Get tools from a specific server\r\n */\r\n getServerTools(serverId: string): MCPTool[] {\r\n const client = this.clients.get(serverId);\r\n return client?.tools || [];\r\n }\r\n\r\n /**\r\n * Call a tool on a specific server\r\n */\r\n async callTool(\r\n serverId: string,\r\n toolName: string,\r\n args?: Record<string, unknown>\r\n ): Promise<MCPToolResult> {\r\n const client = this.clients.get(serverId);\r\n\r\n if (!client) {\r\n return {\r\n success: false,\r\n error: `MCP server not found: ${serverId}`,\r\n };\r\n }\r\n\r\n if (client.status !== \"connected\") {\r\n return {\r\n success: false,\r\n error: `MCP server not connected: ${serverId}`,\r\n };\r\n }\r\n\r\n console.log(`[MCP] Calling ${serverId}:${toolName}`);\r\n return client.callTool(toolName, args);\r\n }\r\n\r\n // ============================================\r\n // SERVER MANAGEMENT\r\n // ============================================\r\n\r\n /**\r\n * Get status of all servers\r\n */\r\n getServerStates(): MCPServerState[] {\r\n return Array.from(this.clients.values()).map((client) => client.getState());\r\n }\r\n\r\n /**\r\n * Get status of a specific server\r\n */\r\n getServerState(serverId: string): MCPServerState | undefined {\r\n return this.clients.get(serverId)?.getState();\r\n }\r\n\r\n /**\r\n * Check if a server is connected\r\n */\r\n isConnected(serverId: string): boolean {\r\n const client = this.clients.get(serverId);\r\n return client?.status === \"connected\";\r\n }\r\n\r\n /**\r\n * Refresh tools from all connected servers\r\n */\r\n async refreshAllTools(): Promise<void> {\r\n await Promise.all(\r\n Array.from(this.clients.values())\r\n .filter((client) => client.status === \"connected\")\r\n .map((client) => client.refreshTools())\r\n );\r\n }\r\n\r\n /**\r\n * Add a new server configuration and optionally connect\r\n */\r\n async addServer(config: MCPServerConfig, connect = true): Promise<void> {\r\n if (this.clients.has(config.id)) {\r\n throw new Error(`Server ${config.id} already exists`);\r\n }\r\n\r\n this.config.servers.push(config);\r\n\r\n if (connect && config.enabled) {\r\n await this.connectServer(config);\r\n }\r\n }\r\n\r\n /**\r\n * Remove a server\r\n */\r\n async removeServer(serverId: string): Promise<void> {\r\n await this.disconnectServer(serverId);\r\n this.config.servers = this.config.servers.filter((s) => s.id !== serverId);\r\n }\r\n\r\n /**\r\n * Get the current configuration\r\n */\r\n getConfig(): MCPConfig {\r\n return { ...this.config };\r\n }\r\n\r\n /**\r\n * Get count of connected servers\r\n */\r\n get connectedCount(): number {\r\n return Array.from(this.clients.values()).filter(\r\n (c) => c.status === \"connected\"\r\n ).length;\r\n }\r\n\r\n /**\r\n * Get total tool count across all servers\r\n */\r\n get totalToolCount(): number {\r\n return this.getAllTools().length;\r\n }\r\n}\r\n\r\n// ============================================\r\n// HELPER FUNCTIONS\r\n// ============================================\r\n\r\n/**\r\n * Load MCP configuration from a file\r\n */\r\nexport async function loadMCPConfig(path: string): Promise<MCPConfig> {\r\n try {\r\n const { readFile, access } = await import(\"node:fs/promises\");\r\n try {\r\n await access(path);\r\n } catch {\r\n console.log(`[MCP] Config file not found: ${path}, using empty config`);\r\n return { servers: [] };\r\n }\r\n\r\n const content = await readFile(path, \"utf-8\");\r\n const config = JSON.parse(content) as MCPConfig;\r\n\r\n // Validate config\r\n if (!Array.isArray(config.servers)) {\r\n console.warn(\"[MCP] Invalid config: servers must be an array\");\r\n return { servers: [] };\r\n }\r\n\r\n return config;\r\n } catch (error) {\r\n console.error(\"[MCP] Failed to load config:\", error);\r\n return { servers: [] };\r\n }\r\n}\r\n\r\n/**\r\n * Create and initialize an MCP registry from a config file\r\n */\r\nexport async function initMCPRegistry(configPath: string): Promise<MCPRegistry> {\r\n const config = await loadMCPConfig(configPath);\r\n const registry = new MCPRegistry(config);\r\n await registry.initialize();\r\n return registry;\r\n}\r\n","/**\n * MCP Tool Bridge - Converts MCP tools to Anthropic format\n * Handles tool routing between native and MCP tools\n */\n\nimport type { Tool } from \"@anthropic-ai/sdk/resources/messages\";\nimport type { MCPRegistry } from \"./registry\";\nimport type { MCPTool, MCPToolProperty, MCPToolResult } from \"./types\";\n\n// MCP tool prefix format: mcp_{serverId}_{toolName}\nconst MCP_TOOL_PREFIX = \"mcp_\";\n\n// ============================================\n// TOOL NAME UTILITIES\n// ============================================\n\n/**\n * Create a prefixed tool name for routing\n */\nexport function createMCPToolName(serverId: string, toolName: string): string {\n // Sanitize serverId and toolName to be safe for use as identifiers\n const safeServerId = serverId.replace(/[^a-zA-Z0-9_]/g, \"_\");\n const safeToolName = toolName.replace(/[^a-zA-Z0-9_]/g, \"_\");\n return `${MCP_TOOL_PREFIX}${safeServerId}__${safeToolName}`;\n}\n\n/**\n * Check if a tool name is an MCP tool\n */\nexport function isMCPTool(name: string): boolean {\n return name.startsWith(MCP_TOOL_PREFIX);\n}\n\n/**\n * Parse an MCP tool name to get serverId and original tool name\n */\nexport function parseMCPToolName(name: string): { serverId: string; toolName: string } | null {\n if (!isMCPTool(name)) {\n return null;\n }\n\n const withoutPrefix = name.slice(MCP_TOOL_PREFIX.length);\n const separatorIndex = withoutPrefix.indexOf(\"__\");\n\n if (separatorIndex === -1) {\n return null;\n }\n\n return {\n serverId: withoutPrefix.slice(0, separatorIndex),\n toolName: withoutPrefix.slice(separatorIndex + 2),\n };\n}\n\n// ============================================\n// SCHEMA CONVERSION\n// ============================================\n\n/**\n * Convert MCP tool property to JSON Schema format\n */\nfunction convertProperty(prop: MCPToolProperty): Record<string, unknown> {\n const result: Record<string, unknown> = {\n type: prop.type,\n };\n\n if (prop.description) {\n result.description = prop.description;\n }\n\n if (prop.enum) {\n result.enum = prop.enum;\n }\n\n if (prop.items) {\n result.items = convertProperty(prop.items);\n }\n\n if (prop.properties) {\n result.properties = Object.fromEntries(\n Object.entries(prop.properties).map(([key, value]) => [\n key,\n convertProperty(value),\n ])\n );\n }\n\n if (prop.required) {\n result.required = prop.required;\n }\n\n return result;\n}\n\n/**\n * Convert MCP tool to Anthropic Tool format\n */\nexport function mcpToolToAnthropicTool(\n serverId: string,\n serverName: string,\n tool: MCPTool\n): Tool {\n const properties: Record<string, Record<string, unknown>> = {};\n\n if (tool.inputSchema.properties) {\n for (const [key, value] of Object.entries(tool.inputSchema.properties)) {\n properties[key] = convertProperty(value);\n }\n }\n\n const inputSchema: Tool.InputSchema = {\n type: \"object\",\n properties,\n required: tool.inputSchema.required || [],\n };\n\n // Create a unique name and add server context to description\n const name = createMCPToolName(serverId, tool.name);\n const description = tool.description\n ? `[${serverName}] ${tool.description}`\n : `[${serverName}] ${tool.name}`;\n\n return {\n name,\n description,\n input_schema: inputSchema,\n };\n}\n\n/**\n * Convert all tools from an MCP registry to Anthropic format\n */\nexport function mcpToolsToAnthropicTools(registry: MCPRegistry): Tool[] {\n const tools: Tool[] = [];\n const serverStates = registry.getServerStates();\n\n for (const state of serverStates) {\n if (state.status !== \"connected\") continue;\n\n const serverName = state.serverInfo?.name || state.config.name;\n\n for (const tool of state.tools) {\n tools.push(mcpToolToAnthropicTool(state.config.id, serverName, tool));\n }\n }\n\n return tools;\n}\n\n// ============================================\n// TOOL EXECUTION\n// ============================================\n\n/**\n * Execute an MCP tool call through the registry\n */\nexport async function executeMCPTool(\n registry: MCPRegistry,\n toolName: string,\n args: Record<string, unknown>\n): Promise<MCPToolResult> {\n const parsed = parseMCPToolName(toolName);\n\n if (!parsed) {\n return {\n success: false,\n error: `Invalid MCP tool name: ${toolName}`,\n };\n }\n\n return registry.callTool(parsed.serverId, parsed.toolName, args);\n}\n\n// ============================================\n// TOOL DISCOVERY\n// ============================================\n\n/**\n * Get a summary of available MCP tools for logging/display\n */\nexport function getMCPToolSummary(registry: MCPRegistry): string {\n const states = registry.getServerStates();\n const lines: string[] = [];\n\n for (const state of states) {\n const status = state.status === \"connected\" ? \"✓\" : \"✗\";\n const serverName = state.serverInfo?.name || state.config.name;\n const toolCount = state.tools.length;\n\n lines.push(` ${status} ${serverName}: ${toolCount} tools`);\n\n if (state.status === \"connected\" && state.tools.length > 0) {\n const toolNames = state.tools.map((t) => t.name).join(\", \");\n lines.push(` Tools: ${toolNames}`);\n } else if (state.lastError) {\n lines.push(` Error: ${state.lastError}`);\n }\n }\n\n if (lines.length === 0) {\n return \" No MCP servers configured\";\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Find an MCP tool by its original name (searches all servers)\n */\nexport function findMCPTool(\n registry: MCPRegistry,\n toolName: string\n): { serverId: string; tool: MCPTool } | null {\n const allTools = registry.getAllTools();\n\n for (const { serverId, tool } of allTools) {\n if (tool.name === toolName) {\n return { serverId, tool };\n }\n }\n\n return null;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tools/file-generation/pdf.ts"],"sourcesContent":["import { createWriteStream } from \"fs\";\r\nimport { mkdir } from \"fs/promises\";\r\nimport { dirname, join } from \"path\";\r\nimport { tmpdir } from \"os\";\r\nimport { randomBytes } from \"crypto\";\r\nimport { isPathAllowed } from \"../../utils/paths\";\r\n\r\nexport interface PDFOptions {\r\n title?: string;\r\n author?: string;\r\n format?: \"A4\" | \"Letter\" | \"Legal\";\r\n orientation?: \"portrait\" | \"landscape\";\r\n margins?: {\r\n top: number;\r\n bottom: number;\r\n left: number;\r\n right: number;\r\n };\r\n}\r\n\r\nexport interface PDFGenerationResult {\r\n success: boolean;\r\n filePath?: string;\r\n error?: string;\r\n}\r\n\r\nconst DEFAULT_OPTIONS: PDFOptions = {\r\n format: \"A4\",\r\n orientation: \"portrait\",\r\n margins: { top: 72, bottom: 72, left: 72, right: 72 },\r\n};\r\n\r\n// Generate temp file path\r\nfunction getTempPath(): string {\r\n const id = randomBytes(8).toString(\"hex\");\r\n return join(tmpdir(), `sentinel-doc-${id}.pdf`);\r\n}\r\n\r\n// Simple markdown to text conversion (for basic PDF)\r\nfunction markdownToText(markdown: string): string {\r\n return markdown\r\n // Remove code blocks\r\n .replace(/```[\\s\\S]*?```/g, (match) => {\r\n const code = match.replace(/```\\w*\\n?/g, \"\").trim();\r\n return `\\n${code}\\n`;\r\n })\r\n // Remove inline code\r\n .replace(/`([^`]+)`/g, \"$1\")\r\n // Convert headers\r\n .replace(/^### (.+)$/gm, \"\\n$1\\n\" + \"-\".repeat(30))\r\n .replace(/^## (.+)$/gm, \"\\n$1\\n\" + \"=\".repeat(40))\r\n .replace(/^# (.+)$/gm, \"\\n$1\\n\" + \"=\".repeat(50))\r\n // Convert bold/italic\r\n .replace(/\\*\\*([^*]+)\\*\\*/g, \"$1\")\r\n .replace(/\\*([^*]+)\\*/g, \"$1\")\r\n .replace(/__([^_]+)__/g, \"$1\")\r\n .replace(/_([^_]+)_/g, \"$1\")\r\n // Convert links\r\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, \"$1\")\r\n // Convert lists\r\n .replace(/^[-*] /gm, \"• \")\r\n .replace(/^\\d+\\. /gm, \" \")\r\n // Clean up\r\n .replace(/\\n{3,}/g, \"\\n\\n\");\r\n}\r\n\r\n// Generate PDF natively using PDFKit (no browser needed)\r\nexport async function generatePDFNative(\r\n content: string,\r\n outputPath?: string,\r\n options: PDFOptions = {}\r\n): Promise<PDFGenerationResult> {\r\n const finalOptions = { ...DEFAULT_OPTIONS, ...options };\r\n const filePath = outputPath || getTempPath();\r\n\r\n if (outputPath && !isPathAllowed(outputPath)) {\r\n return { success: false, error: \"Access to this path is not allowed\" };\r\n }\r\n\r\n try {\r\n await mkdir(dirname(filePath), { recursive: true });\r\n\r\n // @ts-expect-error pdfkit has no type declarations\r\n const PDFDocument = (await import(\"pdfkit\")).default;\r\n const doc = new PDFDocument({\r\n size: finalOptions.format || \"A4\",\r\n layout: finalOptions.orientation || \"portrait\",\r\n margins: finalOptions.margins || { top: 72, bottom: 72, left: 72, right: 72 },\r\n info: {\r\n Title: finalOptions.title || \"Document\",\r\n Author: finalOptions.author || \"OpenSentinel\",\r\n },\r\n });\r\n\r\n const stream = createWriteStream(filePath);\r\n doc.pipe(stream);\r\n\r\n // Parse markdown-like content into PDF sections\r\n const lines = content.split(\"\\n\");\r\n for (const line of lines) {\r\n if (line.startsWith(\"# \")) {\r\n doc.fontSize(24).font(\"Helvetica-Bold\").text(line.slice(2), { align: \"left\" });\r\n doc.moveDown(0.5);\r\n } else if (line.startsWith(\"## \")) {\r\n doc.fontSize(18).font(\"Helvetica-Bold\").text(line.slice(3), { align: \"left\" });\r\n doc.moveDown(0.3);\r\n } else if (line.startsWith(\"### \")) {\r\n doc.fontSize(14).font(\"Helvetica-Bold\").text(line.slice(4), { align: \"left\" });\r\n doc.moveDown(0.2);\r\n } else if (line.startsWith(\"- \") || line.startsWith(\"* \")) {\r\n doc.fontSize(12).font(\"Helvetica\").text(` \\u2022 ${line.slice(2)}`, { indent: 20 });\r\n } else if (line.trim() === \"\") {\r\n doc.moveDown(0.5);\r\n } else {\r\n doc.fontSize(12).font(\"Helvetica\").text(line, { align: \"left\" });\r\n }\r\n }\r\n\r\n doc.end();\r\n await new Promise<void>((resolve, reject) => {\r\n stream.on(\"finish\", resolve);\r\n stream.on(\"error\", reject);\r\n });\r\n\r\n return { success: true, filePath };\r\n } catch (error) {\r\n return {\r\n success: false,\r\n error: error instanceof Error ? error.message : String(error),\r\n };\r\n }\r\n}\r\n\r\n// Generate PDF from markdown content using PDFKit\r\nexport async function generatePDFFromMarkdown(\r\n markdown: string,\r\n outputPath?: string,\r\n options: PDFOptions = {}\r\n): Promise<PDFGenerationResult> {\r\n // Use native PDFKit implementation\r\n return generatePDFNative(markdown, outputPath, options);\r\n}\r\n\r\n// Generate PDF from HTML (requires browser/puppeteer)\r\nexport async function generatePDFFromHTML(\r\n html: string,\r\n outputPath?: string,\r\n options: PDFOptions = {}\r\n): Promise<PDFGenerationResult> {\r\n const filePath = outputPath || getTempPath();\r\n\r\n // Security check\r\n if (outputPath && !isPathAllowed(outputPath)) {\r\n return {\r\n success: false,\r\n error: \"Access to this path is not allowed\",\r\n };\r\n }\r\n\r\n try {\r\n // Ensure directory exists\r\n await mkdir(dirname(filePath), { recursive: true });\r\n\r\n // Try to use Playwright if available\r\n try {\r\n const { chromium } = await import(\"playwright\");\r\n const browser = await chromium.launch({ headless: true });\r\n const page = await browser.newPage();\r\n\r\n await page.setContent(html, { waitUntil: \"networkidle\" });\r\n\r\n await page.pdf({\r\n path: filePath,\r\n format: options.format || \"A4\",\r\n landscape: options.orientation === \"landscape\",\r\n margin: options.margins\r\n ? {\r\n top: `${options.margins.top}px`,\r\n bottom: `${options.margins.bottom}px`,\r\n left: `${options.margins.left}px`,\r\n right: `${options.margins.right}px`,\r\n }\r\n : undefined,\r\n });\r\n\r\n await browser.close();\r\n\r\n return { success: true, filePath };\r\n } catch {\r\n // Fallback: save as HTML\r\n const { writeFile } = await import(\"fs/promises\");\r\n const htmlPath = filePath.replace(\".pdf\", \".html\");\r\n await writeFile(htmlPath, html, \"utf-8\");\r\n\r\n return {\r\n success: true,\r\n filePath: htmlPath,\r\n };\r\n }\r\n } catch (error) {\r\n return {\r\n success: false,\r\n error: error instanceof Error ? error.message : String(error),\r\n };\r\n }\r\n}\r\n\r\n// Main function for tool use\r\nexport async function generatePDF(\r\n content: string,\r\n filename: string,\r\n options?: PDFOptions & { contentType?: \"markdown\" | \"html\" }\r\n): Promise<PDFGenerationResult> {\r\n const outputPath = isPathAllowed(filename) ? filename : join(tmpdir(), filename);\r\n const contentType = options?.contentType || \"markdown\";\r\n\r\n if (contentType === \"html\") {\r\n return generatePDFFromHTML(content, outputPath, options);\r\n }\r\n\r\n return generatePDFFromMarkdown(content, outputPath, options);\r\n}\r\n\r\nexport default {\r\n generatePDF,\r\n generatePDFNative,\r\n generatePDFFromMarkdown,\r\n generatePDFFromHTML,\r\n};\r\n"],"mappings":";;;;;AAAA,SAAS,yBAAyB;AAClC,SAAS,aAAa;AACtB,SAAS,SAAS,YAAY;AAC9B,SAAS,cAAc;AACvB,SAAS,mBAAmB;AAsB5B,IAAM,kBAA8B;AAAA,EAClC,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,SAAS,EAAE,KAAK,IAAI,QAAQ,IAAI,MAAM,IAAI,OAAO,GAAG;AACtD;AAGA,SAAS,cAAsB;AAC7B,QAAM,KAAK,YAAY,CAAC,EAAE,SAAS,KAAK;AACxC,SAAO,KAAK,OAAO,GAAG,gBAAgB,EAAE,MAAM;AAChD;AA+BA,eAAsB,kBACpB,SACA,YACA,UAAsB,CAAC,GACO;AAC9B,QAAM,eAAe,EAAE,GAAG,iBAAiB,GAAG,QAAQ;AACtD,QAAM,WAAW,cAAc,YAAY;AAE3C,MAAI,cAAc,CAAC,cAAc,UAAU,GAAG;AAC5C,WAAO,EAAE,SAAS,OAAO,OAAO,qCAAqC;AAAA,EACvE;AAEA,MAAI;AACF,UAAM,MAAM,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAGlD,UAAM,eAAe,MAAM,OAAO,QAAQ,GAAG;AAC7C,UAAM,MAAM,IAAI,YAAY;AAAA,MAC1B,MAAM,aAAa,UAAU;AAAA,MAC7B,QAAQ,aAAa,eAAe;AAAA,MACpC,SAAS,aAAa,WAAW,EAAE,KAAK,IAAI,QAAQ,IAAI,MAAM,IAAI,OAAO,GAAG;AAAA,MAC5E,MAAM;AAAA,QACJ,OAAO,aAAa,SAAS;AAAA,QAC7B,QAAQ,aAAa,UAAU;AAAA,MACjC;AAAA,IACF,CAAC;AAED,UAAM,SAAS,kBAAkB,QAAQ;AACzC,QAAI,KAAK,MAAM;AAGf,UAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,eAAW,QAAQ,OAAO;AACxB,UAAI,KAAK,WAAW,IAAI,GAAG;AACzB,YAAI,SAAS,EAAE,EAAE,KAAK,gBAAgB,EAAE,KAAK,KAAK,MAAM,CAAC,GAAG,EAAE,OAAO,OAAO,CAAC;AAC7E,YAAI,SAAS,GAAG;AAAA,MAClB,WAAW,KAAK,WAAW,KAAK,GAAG;AACjC,YAAI,SAAS,EAAE,EAAE,KAAK,gBAAgB,EAAE,KAAK,KAAK,MAAM,CAAC,GAAG,EAAE,OAAO,OAAO,CAAC;AAC7E,YAAI,SAAS,GAAG;AAAA,MAClB,WAAW,KAAK,WAAW,MAAM,GAAG;AAClC,YAAI,SAAS,EAAE,EAAE,KAAK,gBAAgB,EAAE,KAAK,KAAK,MAAM,CAAC,GAAG,EAAE,OAAO,OAAO,CAAC;AAC7E,YAAI,SAAS,GAAG;AAAA,MAClB,WAAW,KAAK,WAAW,IAAI,KAAK,KAAK,WAAW,IAAI,GAAG;AACzD,YAAI,SAAS,EAAE,EAAE,KAAK,WAAW,EAAE,KAAK,YAAY,KAAK,MAAM,CAAC,CAAC,IAAI,EAAE,QAAQ,GAAG,CAAC;AAAA,MACrF,WAAW,KAAK,KAAK,MAAM,IAAI;AAC7B,YAAI,SAAS,GAAG;AAAA,MAClB,OAAO;AACL,YAAI,SAAS,EAAE,EAAE,KAAK,WAAW,EAAE,KAAK,MAAM,EAAE,OAAO,OAAO,CAAC;AAAA,MACjE;AAAA,IACF;AAEA,QAAI,IAAI;AACR,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,aAAO,GAAG,UAAU,OAAO;AAC3B,aAAO,GAAG,SAAS,MAAM;AAAA,IAC3B,CAAC;AAED,WAAO,EAAE,SAAS,MAAM,SAAS;AAAA,EACnC,SAAS,OAAO;AACd,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,IAC9D;AAAA,EACF;AACF;AAGA,eAAsB,wBACpB,UACA,YACA,UAAsB,CAAC,GACO;AAE9B,SAAO,kBAAkB,UAAU,YAAY,OAAO;AACxD;AAGA,eAAsB,oBACpB,MACA,YACA,UAAsB,CAAC,GACO;AAC9B,QAAM,WAAW,cAAc,YAAY;AAG3C,MAAI,cAAc,CAAC,cAAc,UAAU,GAAG;AAC5C,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO;AAAA,IACT;AAAA,EACF;AAEA,MAAI;AAEF,UAAM,MAAM,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAGlD,QAAI;AACF,YAAM,EAAE,SAAS,IAAI,MAAM,OAAO,YAAY;AAC9C,YAAM,UAAU,MAAM,SAAS,OAAO,EAAE,UAAU,KAAK,CAAC;AACxD,YAAM,OAAO,MAAM,QAAQ,QAAQ;AAEnC,YAAM,KAAK,WAAW,MAAM,EAAE,WAAW,cAAc,CAAC;AAExD,YAAM,KAAK,IAAI;AAAA,QACb,MAAM;AAAA,QACN,QAAQ,QAAQ,UAAU;AAAA,QAC1B,WAAW,QAAQ,gBAAgB;AAAA,QACnC,QAAQ,QAAQ,UACZ;AAAA,UACE,KAAK,GAAG,QAAQ,QAAQ,GAAG;AAAA,UAC3B,QAAQ,GAAG,QAAQ,QAAQ,MAAM;AAAA,UACjC,MAAM,GAAG,QAAQ,QAAQ,IAAI;AAAA,UAC7B,OAAO,GAAG,QAAQ,QAAQ,KAAK;AAAA,QACjC,IACA;AAAA,MACN,CAAC;AAED,YAAM,QAAQ,MAAM;AAEpB,aAAO,EAAE,SAAS,MAAM,SAAS;AAAA,IACnC,QAAQ;AAEN,YAAM,EAAE,UAAU,IAAI,MAAM,OAAO,aAAa;AAChD,YAAM,WAAW,SAAS,QAAQ,QAAQ,OAAO;AACjD,YAAM,UAAU,UAAU,MAAM,OAAO;AAEvC,aAAO;AAAA,QACL,SAAS;AAAA,QACT,UAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,IAC9D;AAAA,EACF;AACF;AAGA,eAAsB,YACpB,SACA,UACA,SAC8B;AAC9B,QAAM,aAAa,cAAc,QAAQ,IAAI,WAAW,KAAK,OAAO,GAAG,QAAQ;AAC/E,QAAM,cAAc,SAAS,eAAe;AAE5C,MAAI,gBAAgB,QAAQ;AAC1B,WAAO,oBAAoB,SAAS,YAAY,OAAO;AAAA,EACzD;AAEA,SAAO,wBAAwB,SAAS,YAAY,OAAO;AAC7D;AAEA,IAAO,cAAQ;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;","names":[]}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import {
|
|
2
|
+
env
|
|
3
|
+
} from "./chunk-4KIHDIXZ.js";
|
|
4
|
+
|
|
5
|
+
// src/core/security/field-encryption.ts
|
|
6
|
+
import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
|
|
7
|
+
var ALGORITHM = "aes-256-gcm";
|
|
8
|
+
var IV_LENGTH = 16;
|
|
9
|
+
var TAG_LENGTH = 16;
|
|
10
|
+
var KEY_LENGTH = 32;
|
|
11
|
+
var CURRENT_KEY_VERSION = 1;
|
|
12
|
+
var _masterKey = null;
|
|
13
|
+
function getMasterKey() {
|
|
14
|
+
if (_masterKey) return _masterKey;
|
|
15
|
+
const keyBase64 = env.ENCRYPTION_MASTER_KEY;
|
|
16
|
+
if (keyBase64) {
|
|
17
|
+
const key = Buffer.from(keyBase64, "base64");
|
|
18
|
+
if (key.length !== KEY_LENGTH) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
`ENCRYPTION_MASTER_KEY must be ${KEY_LENGTH} bytes (${KEY_LENGTH * 4 / 3} base64 chars). Got ${key.length} bytes.`
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
_masterKey = key;
|
|
24
|
+
return _masterKey;
|
|
25
|
+
}
|
|
26
|
+
if (env.NODE_ENV === "production") {
|
|
27
|
+
throw new Error(
|
|
28
|
+
`ENCRYPTION_MASTER_KEY is required in production for SOC 2 compliance. Generate one with: node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
console.warn("[field-encryption] No ENCRYPTION_MASTER_KEY set \u2014 using ephemeral key (dev only)");
|
|
32
|
+
_masterKey = randomBytes(KEY_LENGTH);
|
|
33
|
+
return _masterKey;
|
|
34
|
+
}
|
|
35
|
+
function encryptField(plaintext) {
|
|
36
|
+
if (plaintext === null || plaintext === void 0) return null;
|
|
37
|
+
const key = getMasterKey();
|
|
38
|
+
const iv = randomBytes(IV_LENGTH);
|
|
39
|
+
const cipher = createCipheriv(ALGORITHM, key, iv);
|
|
40
|
+
const encrypted = Buffer.concat([
|
|
41
|
+
cipher.update(plaintext, "utf8"),
|
|
42
|
+
cipher.final()
|
|
43
|
+
]);
|
|
44
|
+
const tag = cipher.getAuthTag();
|
|
45
|
+
const combined = Buffer.concat([
|
|
46
|
+
Buffer.from([CURRENT_KEY_VERSION]),
|
|
47
|
+
iv,
|
|
48
|
+
tag,
|
|
49
|
+
encrypted
|
|
50
|
+
]);
|
|
51
|
+
return combined.toString("base64");
|
|
52
|
+
}
|
|
53
|
+
function decryptField(encryptedBase64) {
|
|
54
|
+
if (encryptedBase64 === null || encryptedBase64 === void 0) return null;
|
|
55
|
+
const combined = Buffer.from(encryptedBase64, "base64");
|
|
56
|
+
if (combined.length < 1 + IV_LENGTH + TAG_LENGTH + 1) {
|
|
57
|
+
throw new Error("Encrypted data too short");
|
|
58
|
+
}
|
|
59
|
+
const keyVersion = combined[0];
|
|
60
|
+
if (keyVersion !== CURRENT_KEY_VERSION) {
|
|
61
|
+
throw new Error(`Unsupported encryption key version: ${keyVersion}`);
|
|
62
|
+
}
|
|
63
|
+
const iv = combined.subarray(1, 1 + IV_LENGTH);
|
|
64
|
+
const tag = combined.subarray(1 + IV_LENGTH, 1 + IV_LENGTH + TAG_LENGTH);
|
|
65
|
+
const ciphertext = combined.subarray(1 + IV_LENGTH + TAG_LENGTH);
|
|
66
|
+
const key = getMasterKey();
|
|
67
|
+
const decipher = createDecipheriv(ALGORITHM, key, iv);
|
|
68
|
+
decipher.setAuthTag(tag);
|
|
69
|
+
const decrypted = Buffer.concat([
|
|
70
|
+
decipher.update(ciphertext),
|
|
71
|
+
decipher.final()
|
|
72
|
+
]);
|
|
73
|
+
return decrypted.toString("utf8");
|
|
74
|
+
}
|
|
75
|
+
function isEncryptionAvailable() {
|
|
76
|
+
try {
|
|
77
|
+
getMasterKey();
|
|
78
|
+
return true;
|
|
79
|
+
} catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export {
|
|
85
|
+
encryptField,
|
|
86
|
+
decryptField,
|
|
87
|
+
isEncryptionAvailable
|
|
88
|
+
};
|
|
89
|
+
//# sourceMappingURL=chunk-DTISLIMB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/security/field-encryption.ts"],"sourcesContent":["/**\r\n * Field-Level Encryption for SOC 2 Compliance\r\n *\r\n * AES-256-GCM encryption for database column values.\r\n * Used to encrypt sensitive fields (messages, memories) at rest.\r\n *\r\n * Format: base64(keyVersion[1] + iv[16] + authTag[16] + ciphertext)\r\n */\r\n\r\nimport { createCipheriv, createDecipheriv, randomBytes } from \"crypto\";\r\nimport { env } from \"../../config/env\";\r\n\r\nconst ALGORITHM = \"aes-256-gcm\";\r\nconst IV_LENGTH = 16;\r\nconst TAG_LENGTH = 16;\r\nconst KEY_LENGTH = 32;\r\nconst CURRENT_KEY_VERSION = 1;\r\n\r\nlet _masterKey: Buffer | null = null;\r\n\r\nfunction getMasterKey(): Buffer {\r\n if (_masterKey) return _masterKey;\r\n\r\n const keyBase64 = env.ENCRYPTION_MASTER_KEY;\r\n\r\n if (keyBase64) {\r\n const key = Buffer.from(keyBase64, \"base64\");\r\n if (key.length !== KEY_LENGTH) {\r\n throw new Error(\r\n `ENCRYPTION_MASTER_KEY must be ${KEY_LENGTH} bytes (${KEY_LENGTH * 4 / 3} base64 chars). Got ${key.length} bytes.`\r\n );\r\n }\r\n _masterKey = key;\r\n return _masterKey;\r\n }\r\n\r\n // No key configured — in production this is an error\r\n if (env.NODE_ENV === \"production\") {\r\n throw new Error(\r\n \"ENCRYPTION_MASTER_KEY is required in production for SOC 2 compliance. \" +\r\n \"Generate one with: node -e \\\"console.log(require('crypto').randomBytes(32).toString('base64'))\\\"\"\r\n );\r\n }\r\n\r\n // Dev/test: generate ephemeral key (data won't survive restarts)\r\n console.warn(\"[field-encryption] No ENCRYPTION_MASTER_KEY set — using ephemeral key (dev only)\");\r\n _masterKey = randomBytes(KEY_LENGTH);\r\n return _masterKey;\r\n}\r\n\r\n/**\r\n * Encrypt a field value using AES-256-GCM.\r\n * Returns base64(keyVersion + iv + authTag + ciphertext), or null if input is null.\r\n */\r\nexport function encryptField(plaintext: string | null): string | null {\r\n if (plaintext === null || plaintext === undefined) return null;\r\n\r\n const key = getMasterKey();\r\n const iv = randomBytes(IV_LENGTH);\r\n const cipher = createCipheriv(ALGORITHM, key, iv);\r\n\r\n const encrypted = Buffer.concat([\r\n cipher.update(plaintext, \"utf8\"),\r\n cipher.final(),\r\n ]);\r\n const tag = cipher.getAuthTag();\r\n\r\n // keyVersion (1 byte) + iv (16) + tag (16) + ciphertext\r\n const combined = Buffer.concat([\r\n Buffer.from([CURRENT_KEY_VERSION]),\r\n iv,\r\n tag,\r\n encrypted,\r\n ]);\r\n\r\n return combined.toString(\"base64\");\r\n}\r\n\r\n/**\r\n * Decrypt a field value encrypted by encryptField().\r\n * Returns plaintext, or null if input is null.\r\n */\r\nexport function decryptField(encryptedBase64: string | null): string | null {\r\n if (encryptedBase64 === null || encryptedBase64 === undefined) return null;\r\n\r\n const combined = Buffer.from(encryptedBase64, \"base64\");\r\n\r\n if (combined.length < 1 + IV_LENGTH + TAG_LENGTH + 1) {\r\n throw new Error(\"Encrypted data too short\");\r\n }\r\n\r\n const keyVersion = combined[0];\r\n if (keyVersion !== CURRENT_KEY_VERSION) {\r\n throw new Error(`Unsupported encryption key version: ${keyVersion}`);\r\n }\r\n\r\n const iv = combined.subarray(1, 1 + IV_LENGTH);\r\n const tag = combined.subarray(1 + IV_LENGTH, 1 + IV_LENGTH + TAG_LENGTH);\r\n const ciphertext = combined.subarray(1 + IV_LENGTH + TAG_LENGTH);\r\n\r\n const key = getMasterKey();\r\n const decipher = createDecipheriv(ALGORITHM, key, iv);\r\n decipher.setAuthTag(tag);\r\n\r\n const decrypted = Buffer.concat([\r\n decipher.update(ciphertext),\r\n decipher.final(),\r\n ]);\r\n\r\n return decrypted.toString(\"utf8\");\r\n}\r\n\r\n/**\r\n * Encrypt multiple fields at once.\r\n */\r\nexport function encryptFields(\r\n fields: Record<string, string | null>\r\n): Record<string, string | null> {\r\n const result: Record<string, string | null> = {};\r\n for (const [k, v] of Object.entries(fields)) {\r\n result[k] = encryptField(v);\r\n }\r\n return result;\r\n}\r\n\r\n/**\r\n * Decrypt multiple fields at once.\r\n */\r\nexport function decryptFields(\r\n fields: Record<string, string | null>\r\n): Record<string, string | null> {\r\n const result: Record<string, string | null> = {};\r\n for (const [k, v] of Object.entries(fields)) {\r\n result[k] = decryptField(v);\r\n }\r\n return result;\r\n}\r\n\r\n/**\r\n * Generate a new random 32-byte encryption key, returned as base64.\r\n * Use this to create an ENCRYPTION_MASTER_KEY for .env.\r\n */\r\nexport function generateEncryptionKey(): string {\r\n return randomBytes(KEY_LENGTH).toString(\"base64\");\r\n}\r\n\r\n/**\r\n * Check whether field encryption is available (master key is configured).\r\n */\r\nexport function isEncryptionAvailable(): boolean {\r\n try {\r\n getMasterKey();\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n"],"mappings":";;;;;AASA,SAAS,gBAAgB,kBAAkB,mBAAmB;AAG9D,IAAM,YAAY;AAClB,IAAM,YAAY;AAClB,IAAM,aAAa;AACnB,IAAM,aAAa;AACnB,IAAM,sBAAsB;AAE5B,IAAI,aAA4B;AAEhC,SAAS,eAAuB;AAC9B,MAAI,WAAY,QAAO;AAEvB,QAAM,YAAY,IAAI;AAEtB,MAAI,WAAW;AACb,UAAM,MAAM,OAAO,KAAK,WAAW,QAAQ;AAC3C,QAAI,IAAI,WAAW,YAAY;AAC7B,YAAM,IAAI;AAAA,QACR,iCAAiC,UAAU,WAAW,aAAa,IAAI,CAAC,uBAAuB,IAAI,MAAM;AAAA,MAC3G;AAAA,IACF;AACA,iBAAa;AACb,WAAO;AAAA,EACT;AAGA,MAAI,IAAI,aAAa,cAAc;AACjC,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AAGA,UAAQ,KAAK,uFAAkF;AAC/F,eAAa,YAAY,UAAU;AACnC,SAAO;AACT;AAMO,SAAS,aAAa,WAAyC;AACpE,MAAI,cAAc,QAAQ,cAAc,OAAW,QAAO;AAE1D,QAAM,MAAM,aAAa;AACzB,QAAM,KAAK,YAAY,SAAS;AAChC,QAAM,SAAS,eAAe,WAAW,KAAK,EAAE;AAEhD,QAAM,YAAY,OAAO,OAAO;AAAA,IAC9B,OAAO,OAAO,WAAW,MAAM;AAAA,IAC/B,OAAO,MAAM;AAAA,EACf,CAAC;AACD,QAAM,MAAM,OAAO,WAAW;AAG9B,QAAM,WAAW,OAAO,OAAO;AAAA,IAC7B,OAAO,KAAK,CAAC,mBAAmB,CAAC;AAAA,IACjC;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,SAAO,SAAS,SAAS,QAAQ;AACnC;AAMO,SAAS,aAAa,iBAA+C;AAC1E,MAAI,oBAAoB,QAAQ,oBAAoB,OAAW,QAAO;AAEtE,QAAM,WAAW,OAAO,KAAK,iBAAiB,QAAQ;AAEtD,MAAI,SAAS,SAAS,IAAI,YAAY,aAAa,GAAG;AACpD,UAAM,IAAI,MAAM,0BAA0B;AAAA,EAC5C;AAEA,QAAM,aAAa,SAAS,CAAC;AAC7B,MAAI,eAAe,qBAAqB;AACtC,UAAM,IAAI,MAAM,uCAAuC,UAAU,EAAE;AAAA,EACrE;AAEA,QAAM,KAAK,SAAS,SAAS,GAAG,IAAI,SAAS;AAC7C,QAAM,MAAM,SAAS,SAAS,IAAI,WAAW,IAAI,YAAY,UAAU;AACvE,QAAM,aAAa,SAAS,SAAS,IAAI,YAAY,UAAU;AAE/D,QAAM,MAAM,aAAa;AACzB,QAAM,WAAW,iBAAiB,WAAW,KAAK,EAAE;AACpD,WAAS,WAAW,GAAG;AAEvB,QAAM,YAAY,OAAO,OAAO;AAAA,IAC9B,SAAS,OAAO,UAAU;AAAA,IAC1B,SAAS,MAAM;AAAA,EACjB,CAAC;AAED,SAAO,UAAU,SAAS,MAAM;AAClC;AAuCO,SAAS,wBAAiC;AAC/C,MAAI;AACF,iBAAa;AACb,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
chat
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-ZMML6T63.js";
|
|
4
4
|
|
|
5
5
|
// src/integrations/email/email-parser.ts
|
|
6
6
|
function parseEmail(email) {
|
|
@@ -774,4 +774,4 @@ export {
|
|
|
774
774
|
analyzeSentiment,
|
|
775
775
|
inbox_summarizer_default
|
|
776
776
|
};
|
|
777
|
-
//# sourceMappingURL=chunk-
|
|
777
|
+
//# sourceMappingURL=chunk-GBVJTRXS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/integrations/email/email-parser.ts","../src/integrations/email/inbox-summarizer.ts"],"sourcesContent":["import type { EmailMessage, EmailAttachment, EmailAddress } from \"./imap-client\";\r\n\r\nexport interface ParsedEmail {\r\n id: string;\r\n subject: string;\r\n from: EmailAddress[];\r\n to: EmailAddress[];\r\n cc: EmailAddress[];\r\n date: Date;\r\n body: {\r\n text: string;\r\n html: string;\r\n snippet: string;\r\n };\r\n thread: ThreadInfo;\r\n attachments: ParsedAttachment[];\r\n metadata: EmailMetadata;\r\n}\r\n\r\nexport interface ThreadInfo {\r\n id: string;\r\n messageId: string;\r\n inReplyTo?: string;\r\n references: string[];\r\n position: number;\r\n isReply: boolean;\r\n isForward: boolean;\r\n}\r\n\r\nexport interface ParsedAttachment {\r\n filename: string;\r\n contentType: string;\r\n size: number;\r\n isInline: boolean;\r\n cid?: string;\r\n content?: Buffer;\r\n extension: string;\r\n category: \"image\" | \"document\" | \"archive\" | \"media\" | \"other\";\r\n}\r\n\r\nexport interface EmailMetadata {\r\n isRead: boolean;\r\n isFlagged: boolean;\r\n isSpam: boolean;\r\n isDraft: boolean;\r\n labels: string[];\r\n importance: \"high\" | \"normal\" | \"low\";\r\n hasAttachments: boolean;\r\n attachmentCount: number;\r\n totalAttachmentSize: number;\r\n}\r\n\r\nexport interface EmailThread {\r\n id: string;\r\n subject: string;\r\n participants: EmailAddress[];\r\n messageCount: number;\r\n unreadCount: number;\r\n lastMessageDate: Date;\r\n firstMessageDate: Date;\r\n messages: ParsedEmail[];\r\n snippet: string;\r\n}\r\n\r\nexport interface QuotedSection {\r\n level: number;\r\n content: string;\r\n attribution?: string;\r\n}\r\n\r\nexport interface EmailBodyParts {\r\n newContent: string;\r\n quotedContent: QuotedSection[];\r\n signature?: string;\r\n}\r\n\r\n/**\r\n * Parse an email message into a more structured format\r\n */\r\nexport function parseEmail(email: EmailMessage): ParsedEmail {\r\n const isReply = email.subject.toLowerCase().startsWith(\"re:\");\r\n const isForward = email.subject.toLowerCase().startsWith(\"fwd:\") ||\r\n email.subject.toLowerCase().startsWith(\"fw:\");\r\n\r\n // Calculate attachment metadata\r\n const attachments = email.attachments.map(att => parseAttachment(att));\r\n const totalAttachmentSize = attachments.reduce((sum, att) => sum + att.size, 0);\r\n\r\n // Determine importance from headers or flags\r\n let importance: \"high\" | \"normal\" | \"low\" = \"normal\";\r\n const importanceHeader = email.headers.get(\"importance\") ||\r\n email.headers.get(\"x-priority\");\r\n if (importanceHeader) {\r\n const lowerHeader = importanceHeader.toLowerCase();\r\n if (lowerHeader.includes(\"high\") || lowerHeader === \"1\" || lowerHeader === \"2\") {\r\n importance = \"high\";\r\n } else if (lowerHeader.includes(\"low\") || lowerHeader === \"5\") {\r\n importance = \"low\";\r\n }\r\n }\r\n\r\n return {\r\n id: email.id,\r\n subject: email.subject,\r\n from: email.from,\r\n to: email.to,\r\n cc: email.cc,\r\n date: email.date,\r\n body: {\r\n text: email.text,\r\n html: email.html,\r\n snippet: email.snippet || email.text.substring(0, 200).replace(/\\s+/g, \" \").trim(),\r\n },\r\n thread: {\r\n id: email.threadId || email.messageId,\r\n messageId: email.messageId,\r\n inReplyTo: email.inReplyTo,\r\n references: email.references,\r\n position: email.references.length,\r\n isReply,\r\n isForward,\r\n },\r\n attachments,\r\n metadata: {\r\n isRead: email.flags.includes(\"\\\\Seen\"),\r\n isFlagged: email.flags.includes(\"\\\\Flagged\"),\r\n isSpam: email.labels.includes(\"Spam\") || email.labels.includes(\"Junk\"),\r\n isDraft: email.flags.includes(\"\\\\Draft\"),\r\n labels: email.labels,\r\n importance,\r\n hasAttachments: attachments.length > 0,\r\n attachmentCount: attachments.length,\r\n totalAttachmentSize,\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Parse an attachment into structured format\r\n */\r\nexport function parseAttachment(attachment: EmailAttachment): ParsedAttachment {\r\n const extension = attachment.filename.includes(\".\")\r\n ? attachment.filename.split(\".\").pop()?.toLowerCase() || \"\"\r\n : \"\";\r\n\r\n return {\r\n filename: attachment.filename,\r\n contentType: attachment.contentType,\r\n size: attachment.size,\r\n isInline: !!attachment.contentId,\r\n cid: attachment.contentId,\r\n content: attachment.content,\r\n extension,\r\n category: categorizeAttachment(attachment.contentType, extension),\r\n };\r\n}\r\n\r\n/**\r\n * Categorize an attachment by its type\r\n */\r\nfunction categorizeAttachment(\r\n contentType: string,\r\n extension: string\r\n): \"image\" | \"document\" | \"archive\" | \"media\" | \"other\" {\r\n const lowerContentType = contentType.toLowerCase();\r\n\r\n // Images\r\n if (lowerContentType.startsWith(\"image/\")) {\r\n return \"image\";\r\n }\r\n\r\n // Documents\r\n const docExtensions = [\"pdf\", \"doc\", \"docx\", \"xls\", \"xlsx\", \"ppt\", \"pptx\", \"txt\", \"rtf\", \"odt\", \"ods\", \"odp\", \"csv\"];\r\n const docMimeTypes = [\r\n \"application/pdf\",\r\n \"application/msword\",\r\n \"application/vnd.openxmlformats\",\r\n \"application/vnd.ms-\",\r\n \"text/plain\",\r\n \"text/csv\",\r\n \"application/vnd.oasis.opendocument\",\r\n ];\r\n if (docExtensions.includes(extension) || docMimeTypes.some(m => lowerContentType.includes(m))) {\r\n return \"document\";\r\n }\r\n\r\n // Archives\r\n const archiveExtensions = [\"zip\", \"rar\", \"7z\", \"tar\", \"gz\", \"bz2\"];\r\n const archiveMimeTypes = [\"application/zip\", \"application/x-rar\", \"application/x-7z\", \"application/gzip\"];\r\n if (archiveExtensions.includes(extension) || archiveMimeTypes.some(m => lowerContentType.includes(m))) {\r\n return \"archive\";\r\n }\r\n\r\n // Media (audio/video)\r\n if (lowerContentType.startsWith(\"audio/\") || lowerContentType.startsWith(\"video/\")) {\r\n return \"media\";\r\n }\r\n\r\n return \"other\";\r\n}\r\n\r\n/**\r\n * Group emails into threads\r\n */\r\nexport function groupIntoThreads(emails: EmailMessage[]): EmailThread[] {\r\n const threadMap = new Map<string, EmailMessage[]>();\r\n\r\n // Group by thread ID (using references chain or Message-ID)\r\n for (const email of emails) {\r\n // Find the root message ID for this thread\r\n let threadId = email.threadId || email.messageId;\r\n\r\n if (email.references.length > 0) {\r\n // Use the first reference as the thread root\r\n threadId = email.references[0];\r\n }\r\n\r\n if (!threadMap.has(threadId)) {\r\n threadMap.set(threadId, []);\r\n }\r\n threadMap.get(threadId)!.push(email);\r\n }\r\n\r\n // Convert to EmailThread objects\r\n const threads: EmailThread[] = [];\r\n\r\n for (const [threadId, messages] of threadMap) {\r\n // Sort messages by date (oldest first)\r\n messages.sort((a, b) => a.date.getTime() - b.date.getTime());\r\n\r\n // Collect all participants\r\n const participantMap = new Map<string, EmailAddress>();\r\n for (const msg of messages) {\r\n for (const addr of [...msg.from, ...msg.to, ...msg.cc]) {\r\n if (!participantMap.has(addr.address)) {\r\n participantMap.set(addr.address, addr);\r\n }\r\n }\r\n }\r\n\r\n // Count unread\r\n const unreadCount = messages.filter(m => !m.flags.includes(\"\\\\Seen\")).length;\r\n\r\n // Get the subject (use the original, non-reply subject if possible)\r\n let subject = messages[0].subject;\r\n for (const msg of messages) {\r\n const cleaned = cleanSubject(msg.subject);\r\n if (cleaned.length > 0 && !cleaned.toLowerCase().startsWith(\"re:\")) {\r\n subject = msg.subject;\r\n break;\r\n }\r\n }\r\n\r\n threads.push({\r\n id: threadId,\r\n subject: cleanSubject(subject),\r\n participants: Array.from(participantMap.values()),\r\n messageCount: messages.length,\r\n unreadCount,\r\n lastMessageDate: messages[messages.length - 1].date,\r\n firstMessageDate: messages[0].date,\r\n messages: messages.map(parseEmail),\r\n snippet: messages[messages.length - 1].snippet ||\r\n messages[messages.length - 1].text.substring(0, 200).replace(/\\s+/g, \" \").trim(),\r\n });\r\n }\r\n\r\n // Sort threads by last message date (newest first)\r\n threads.sort((a, b) => b.lastMessageDate.getTime() - a.lastMessageDate.getTime());\r\n\r\n return threads;\r\n}\r\n\r\n/**\r\n * Clean a subject line (remove Re:, Fwd:, etc.)\r\n */\r\nexport function cleanSubject(subject: string): string {\r\n return subject\r\n .replace(/^(Re|Fwd|Fw):\\s*/gi, \"\")\r\n .replace(/^\\[.*?\\]\\s*/, \"\") // Remove list prefixes like [ListName]\r\n .trim();\r\n}\r\n\r\n/**\r\n * Parse email body to separate new content from quoted sections\r\n */\r\nexport function parseEmailBody(text: string): EmailBodyParts {\r\n const lines = text.split(\"\\n\");\r\n const newLines: string[] = [];\r\n const quotedSections: QuotedSection[] = [];\r\n let currentQuote: { level: number; lines: string[]; attribution?: string } | null = null;\r\n let signature: string | undefined;\r\n let inSignature = false;\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n const line = lines[i];\r\n\r\n // Check for signature delimiter\r\n if (line.trim() === \"--\" || line.trim() === \"-- \") {\r\n inSignature = true;\r\n signature = lines.slice(i + 1).join(\"\\n\").trim();\r\n break;\r\n }\r\n\r\n // Check for quote markers\r\n const quoteMatch = line.match(/^(>+)\\s?/);\r\n\r\n if (quoteMatch) {\r\n const level = quoteMatch[1].length;\r\n const content = line.substring(quoteMatch[0].length);\r\n\r\n if (!currentQuote || currentQuote.level !== level) {\r\n // Save previous quote section\r\n if (currentQuote) {\r\n quotedSections.push({\r\n level: currentQuote.level,\r\n content: currentQuote.lines.join(\"\\n\"),\r\n attribution: currentQuote.attribution,\r\n });\r\n }\r\n currentQuote = { level, lines: [content] };\r\n } else {\r\n currentQuote.lines.push(content);\r\n }\r\n } else if (isAttributionLine(line)) {\r\n // This is an attribution line like \"On Date, Person wrote:\"\r\n if (currentQuote) {\r\n currentQuote.attribution = line;\r\n } else {\r\n // Start of a new quoted section\r\n currentQuote = { level: 1, lines: [], attribution: line };\r\n }\r\n } else {\r\n // Regular line\r\n if (currentQuote) {\r\n quotedSections.push({\r\n level: currentQuote.level,\r\n content: currentQuote.lines.join(\"\\n\"),\r\n attribution: currentQuote.attribution,\r\n });\r\n currentQuote = null;\r\n }\r\n newLines.push(line);\r\n }\r\n }\r\n\r\n // Don't forget the last quote section\r\n if (currentQuote) {\r\n quotedSections.push({\r\n level: currentQuote.level,\r\n content: currentQuote.lines.join(\"\\n\"),\r\n attribution: currentQuote.attribution,\r\n });\r\n }\r\n\r\n return {\r\n newContent: newLines.join(\"\\n\").trim(),\r\n quotedContent: quotedSections,\r\n signature,\r\n };\r\n}\r\n\r\n/**\r\n * Check if a line is an email attribution (e.g., \"On Jan 1, Person wrote:\")\r\n */\r\nfunction isAttributionLine(line: string): boolean {\r\n const attributionPatterns = [\r\n /^On .+ wrote:$/i,\r\n /^On .+, .+ wrote:$/i,\r\n /^.+ wrote:$/i,\r\n /^-{3,}\\s*Original Message\\s*-{3,}$/i,\r\n /^-{3,}\\s*Forwarded message\\s*-{3,}$/i,\r\n /^From:\\s*.+$/i,\r\n /^Sent:\\s*.+$/i,\r\n ];\r\n\r\n return attributionPatterns.some(pattern => pattern.test(line.trim()));\r\n}\r\n\r\n/**\r\n * Extract email addresses from a text string\r\n */\r\nexport function extractEmailAddresses(text: string): string[] {\r\n const emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/g;\r\n const matches = text.match(emailRegex) || [];\r\n return [...new Set(matches)];\r\n}\r\n\r\n/**\r\n * Extract URLs from email text\r\n */\r\nexport function extractUrls(text: string): string[] {\r\n const urlRegex = /https?:\\/\\/[^\\s<>\"\\)]+/gi;\r\n const matches = text.match(urlRegex) || [];\r\n return [...new Set(matches)];\r\n}\r\n\r\n/**\r\n * Extract phone numbers from email text\r\n */\r\nexport function extractPhoneNumbers(text: string): string[] {\r\n const phoneRegex = /(?:\\+\\d{1,3}[-.\\s]?)?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}/g;\r\n const matches = text.match(phoneRegex) || [];\r\n return [...new Set(matches)];\r\n}\r\n\r\n/**\r\n * Get a summary of an email suitable for display\r\n */\r\nexport function getEmailSummary(email: EmailMessage | ParsedEmail): {\r\n from: string;\r\n subject: string;\r\n snippet: string;\r\n date: string;\r\n attachmentCount: number;\r\n} {\r\n const fromAddr = email.from[0];\r\n const fromDisplay = fromAddr\r\n ? fromAddr.name || fromAddr.address\r\n : \"Unknown\";\r\n\r\n const snippet = \"body\" in email\r\n ? email.body.snippet\r\n : email.snippet || email.text.substring(0, 100);\r\n\r\n const attachmentCount = \"metadata\" in email\r\n ? email.metadata.attachmentCount\r\n : email.attachments.length;\r\n\r\n return {\r\n from: fromDisplay,\r\n subject: email.subject,\r\n snippet,\r\n date: formatEmailDate(email.date),\r\n attachmentCount,\r\n };\r\n}\r\n\r\n/**\r\n * Format a date for email display\r\n */\r\nfunction formatEmailDate(date: Date): string {\r\n const now = new Date();\r\n const diff = now.getTime() - date.getTime();\r\n const days = Math.floor(diff / (1000 * 60 * 60 * 24));\r\n\r\n if (days === 0) {\r\n // Today - show time\r\n return date.toLocaleTimeString(\"en-US\", {\r\n hour: \"numeric\",\r\n minute: \"2-digit\",\r\n hour12: true,\r\n });\r\n } else if (days === 1) {\r\n return \"Yesterday\";\r\n } else if (days < 7) {\r\n // This week - show day name\r\n return date.toLocaleDateString(\"en-US\", { weekday: \"short\" });\r\n } else if (date.getFullYear() === now.getFullYear()) {\r\n // This year - show month and day\r\n return date.toLocaleDateString(\"en-US\", { month: \"short\", day: \"numeric\" });\r\n } else {\r\n // Older - show full date\r\n return date.toLocaleDateString(\"en-US\", {\r\n month: \"short\",\r\n day: \"numeric\",\r\n year: \"numeric\",\r\n });\r\n }\r\n}\r\n\r\n/**\r\n * Calculate the size of an email in bytes\r\n */\r\nexport function calculateEmailSize(email: EmailMessage): number {\r\n let size = 0;\r\n\r\n // Text content\r\n size += new TextEncoder().encode(email.text).length;\r\n size += new TextEncoder().encode(email.html).length;\r\n\r\n // Headers (estimate)\r\n size += new TextEncoder().encode(email.subject).length;\r\n size += email.from.reduce((s, a) => s + (a.name?.length || 0) + a.address.length + 10, 0);\r\n size += email.to.reduce((s, a) => s + (a.name?.length || 0) + a.address.length + 10, 0);\r\n\r\n // Attachments\r\n size += email.attachments.reduce((s, a) => s + a.size, 0);\r\n\r\n return size;\r\n}\r\n\r\n/**\r\n * Check if an email matches a filter\r\n */\r\nexport function matchesFilter(\r\n email: EmailMessage | ParsedEmail,\r\n filter: {\r\n from?: string | RegExp;\r\n to?: string | RegExp;\r\n subject?: string | RegExp;\r\n body?: string | RegExp;\r\n hasAttachments?: boolean;\r\n isUnread?: boolean;\r\n isFlagged?: boolean;\r\n dateAfter?: Date;\r\n dateBefore?: Date;\r\n }\r\n): boolean {\r\n const parsed = \"metadata\" in email ? email : parseEmail(email);\r\n\r\n // Check from\r\n if (filter.from) {\r\n const fromMatch = parsed.from.some(addr => {\r\n const str = `${addr.name} ${addr.address}`;\r\n return filter.from instanceof RegExp\r\n ? filter.from.test(str)\r\n : str.toLowerCase().includes(filter.from!.toLowerCase());\r\n });\r\n if (!fromMatch) return false;\r\n }\r\n\r\n // Check to\r\n if (filter.to) {\r\n const toMatch = parsed.to.some(addr => {\r\n const str = `${addr.name} ${addr.address}`;\r\n return filter.to instanceof RegExp\r\n ? filter.to.test(str)\r\n : str.toLowerCase().includes(filter.to!.toLowerCase());\r\n });\r\n if (!toMatch) return false;\r\n }\r\n\r\n // Check subject\r\n if (filter.subject) {\r\n const subjectMatch = filter.subject instanceof RegExp\r\n ? filter.subject.test(parsed.subject)\r\n : parsed.subject.toLowerCase().includes(filter.subject.toLowerCase());\r\n if (!subjectMatch) return false;\r\n }\r\n\r\n // Check body\r\n if (filter.body) {\r\n const bodyText = parsed.body.text;\r\n const bodyMatch = filter.body instanceof RegExp\r\n ? filter.body.test(bodyText)\r\n : bodyText.toLowerCase().includes(filter.body.toLowerCase());\r\n if (!bodyMatch) return false;\r\n }\r\n\r\n // Check attachments\r\n if (filter.hasAttachments !== undefined) {\r\n if (parsed.metadata.hasAttachments !== filter.hasAttachments) return false;\r\n }\r\n\r\n // Check read status\r\n if (filter.isUnread !== undefined) {\r\n if (parsed.metadata.isRead === filter.isUnread) return false;\r\n }\r\n\r\n // Check flagged status\r\n if (filter.isFlagged !== undefined) {\r\n if (parsed.metadata.isFlagged !== filter.isFlagged) return false;\r\n }\r\n\r\n // Check date range\r\n if (filter.dateAfter && parsed.date < filter.dateAfter) return false;\r\n if (filter.dateBefore && parsed.date > filter.dateBefore) return false;\r\n\r\n return true;\r\n}\r\n\r\nexport default {\r\n parseEmail,\r\n parseAttachment,\r\n groupIntoThreads,\r\n cleanSubject,\r\n parseEmailBody,\r\n extractEmailAddresses,\r\n extractUrls,\r\n extractPhoneNumbers,\r\n getEmailSummary,\r\n calculateEmailSize,\r\n matchesFilter,\r\n};\r\n","import { chat } from \"../../core/brain\";\nimport type { EmailMessage } from \"./imap-client\";\nimport { parseEmail, groupIntoThreads, cleanSubject, parseEmailBody } from \"./email-parser\";\nimport type { ParsedEmail, EmailThread } from \"./email-parser\";\n\nexport interface InboxSummary {\n totalEmails: number;\n unreadCount: number;\n importantCount: number;\n categories: CategorySummary[];\n urgentItems: UrgentItem[];\n actionItems: ActionItem[];\n summary: string;\n generatedAt: Date;\n}\n\nexport interface CategorySummary {\n name: string;\n count: number;\n unreadCount: number;\n topSenders: string[];\n description: string;\n}\n\nexport interface UrgentItem {\n emailId: string;\n subject: string;\n from: string;\n reason: string;\n suggestedAction: string;\n}\n\nexport interface ActionItem {\n emailId: string;\n subject: string;\n from: string;\n action: string;\n priority: \"high\" | \"medium\" | \"low\";\n dueDate?: Date;\n context: string;\n}\n\nexport interface EmailCategorization {\n category: string;\n confidence: number;\n reason: string;\n}\n\nexport interface ThreadSummary {\n threadId: string;\n subject: string;\n participantCount: number;\n messageCount: number;\n summary: string;\n currentStatus: string;\n nextSteps: string[];\n keyDecisions: string[];\n openQuestions: string[];\n}\n\nconst CATEGORIZATION_PROMPT = `You are an email categorization assistant. Analyze the following email and categorize it.\n\nCategories:\n- urgent: Time-sensitive, requires immediate attention\n- action_required: Requires a response or action from the user\n- informational: FYI, newsletters, updates that don't require action\n- meeting: Calendar invites, meeting-related emails\n- financial: Invoices, receipts, financial statements\n- social: Personal emails, social notifications\n- marketing: Promotional emails, newsletters\n- support: Customer support, help desk emails\n- work: Work-related emails that aren't urgent\n- spam: Potential spam or unwanted emails\n\nEmail:\nFrom: {{from}}\nSubject: {{subject}}\nDate: {{date}}\nSnippet: {{snippet}}\n\nRespond in JSON format:\n{\n \"category\": \"category_name\",\n \"confidence\": 0.0-1.0,\n \"reason\": \"brief explanation\"\n}`;\n\nconst ACTION_EXTRACTION_PROMPT = `You are an email assistant that extracts action items. Analyze the following email and identify any actions the recipient needs to take.\n\nEmail:\nFrom: {{from}}\nSubject: {{subject}}\nDate: {{date}}\nBody: {{body}}\n\nFor each action item found, provide:\n- action: A clear, concise description of what needs to be done\n- priority: high (urgent/deadline), medium (important but not urgent), or low (when convenient)\n- dueDate: If a specific date/time is mentioned (ISO format or null)\n- context: Brief context from the email\n\nRespond in JSON format:\n{\n \"actions\": [\n {\n \"action\": \"description\",\n \"priority\": \"high|medium|low\",\n \"dueDate\": \"ISO date or null\",\n \"context\": \"brief context\"\n }\n ]\n}\n\nIf no actions are required, return: { \"actions\": [] }`;\n\nconst INBOX_SUMMARY_PROMPT = `You are an email assistant providing a daily inbox summary. Based on the following email statistics and categories, provide a brief, helpful summary.\n\nStatistics:\n- Total emails: {{totalEmails}}\n- Unread: {{unreadCount}}\n- Important: {{importantCount}}\n\nCategories:\n{{categories}}\n\nUrgent items:\n{{urgentItems}}\n\nProvide a 2-3 sentence summary that:\n1. Highlights the most important items requiring attention\n2. Notes any patterns or notable senders\n3. Suggests prioritization if there are many items\n\nKeep it concise and actionable.`;\n\nconst THREAD_SUMMARY_PROMPT = `You are an email assistant that summarizes email threads. Analyze the following email thread and provide a comprehensive summary.\n\nThread: {{subject}}\nParticipants: {{participants}}\nMessages: {{messageCount}}\n\nMessages (oldest to newest):\n{{messages}}\n\nProvide a summary including:\n1. Brief summary of the entire conversation (2-3 sentences)\n2. Current status of the discussion\n3. Key decisions made (if any)\n4. Open questions that need resolution\n5. Suggested next steps\n\nRespond in JSON format:\n{\n \"summary\": \"conversation summary\",\n \"currentStatus\": \"status description\",\n \"keyDecisions\": [\"decision 1\", \"decision 2\"],\n \"openQuestions\": [\"question 1\", \"question 2\"],\n \"nextSteps\": [\"step 1\", \"step 2\"]\n}`;\n\n/**\n * Categorize a single email using AI\n */\nexport async function categorizeEmail(email: EmailMessage | ParsedEmail): Promise<EmailCategorization> {\n const parsed = \"metadata\" in email ? email : parseEmail(email);\n\n const fromDisplay = parsed.from[0]\n ? `${parsed.from[0].name || \"\"} <${parsed.from[0].address}>`.trim()\n : \"Unknown\";\n\n const prompt = CATEGORIZATION_PROMPT\n .replace(\"{{from}}\", fromDisplay)\n .replace(\"{{subject}}\", parsed.subject)\n .replace(\"{{date}}\", parsed.date.toISOString())\n .replace(\"{{snippet}}\", parsed.body.snippet);\n\n try {\n const response = await chat(\n [{ role: \"user\", content: prompt }],\n \"You are a helpful email categorization assistant. Always respond with valid JSON.\"\n );\n\n const result = JSON.parse(response.content);\n return {\n category: result.category || \"informational\",\n confidence: result.confidence || 0.5,\n reason: result.reason || \"\",\n };\n } catch (err) {\n console.error(\"Failed to categorize email:\", err);\n return {\n category: \"informational\",\n confidence: 0.5,\n reason: \"Failed to categorize\",\n };\n }\n}\n\n/**\n * Extract action items from an email using AI\n */\nexport async function extractActionItems(email: EmailMessage | ParsedEmail): Promise<ActionItem[]> {\n const parsed = \"metadata\" in email ? email : parseEmail(email);\n\n const fromDisplay = parsed.from[0]\n ? `${parsed.from[0].name || \"\"} <${parsed.from[0].address}>`.trim()\n : \"Unknown\";\n\n // Parse the email body to get just the new content (not quoted parts)\n const bodyParts = parseEmailBody(parsed.body.text);\n const relevantBody = bodyParts.newContent || parsed.body.text.substring(0, 2000);\n\n const prompt = ACTION_EXTRACTION_PROMPT\n .replace(\"{{from}}\", fromDisplay)\n .replace(\"{{subject}}\", parsed.subject)\n .replace(\"{{date}}\", parsed.date.toISOString())\n .replace(\"{{body}}\", relevantBody);\n\n try {\n const response = await chat(\n [{ role: \"user\", content: prompt }],\n \"You are a helpful email assistant that extracts action items. Always respond with valid JSON.\"\n );\n\n const result = JSON.parse(response.content);\n\n return (result.actions || []).map((action: {\n action: string;\n priority: string;\n dueDate?: string | null;\n context: string;\n }) => ({\n emailId: parsed.id,\n subject: parsed.subject,\n from: fromDisplay,\n action: action.action,\n priority: ([\"high\", \"medium\", \"low\"].includes(action.priority) ? action.priority : \"medium\") as \"high\" | \"medium\" | \"low\",\n dueDate: action.dueDate ? new Date(action.dueDate) : undefined,\n context: action.context,\n }));\n } catch (err) {\n console.error(\"Failed to extract action items:\", err);\n return [];\n }\n}\n\n/**\n * Summarize an email thread using AI\n */\nexport async function summarizeThread(thread: EmailThread): Promise<ThreadSummary> {\n const participants = thread.participants\n .map(p => p.name || p.address)\n .slice(0, 10)\n .join(\", \");\n\n // Build message content (limited to avoid token limits)\n const messageContent = thread.messages\n .slice(-10) // Last 10 messages\n .map((msg, idx) => {\n const from = msg.from[0]?.name || msg.from[0]?.address || \"Unknown\";\n const bodyParts = parseEmailBody(msg.body.text);\n const content = (bodyParts.newContent || msg.body.snippet).substring(0, 500);\n return `[${idx + 1}] From: ${from}\\nDate: ${msg.date.toISOString()}\\n${content}`;\n })\n .join(\"\\n\\n---\\n\\n\");\n\n const prompt = THREAD_SUMMARY_PROMPT\n .replace(\"{{subject}}\", thread.subject)\n .replace(\"{{participants}}\", participants)\n .replace(\"{{messageCount}}\", String(thread.messageCount))\n .replace(\"{{messages}}\", messageContent);\n\n try {\n const response = await chat(\n [{ role: \"user\", content: prompt }],\n \"You are a helpful email assistant that summarizes email threads. Always respond with valid JSON.\"\n );\n\n const result = JSON.parse(response.content);\n\n return {\n threadId: thread.id,\n subject: thread.subject,\n participantCount: thread.participants.length,\n messageCount: thread.messageCount,\n summary: result.summary || \"No summary available\",\n currentStatus: result.currentStatus || \"Unknown\",\n nextSteps: result.nextSteps || [],\n keyDecisions: result.keyDecisions || [],\n openQuestions: result.openQuestions || [],\n };\n } catch (err) {\n console.error(\"Failed to summarize thread:\", err);\n return {\n threadId: thread.id,\n subject: thread.subject,\n participantCount: thread.participants.length,\n messageCount: thread.messageCount,\n summary: \"Failed to generate summary\",\n currentStatus: \"Unknown\",\n nextSteps: [],\n keyDecisions: [],\n openQuestions: [],\n };\n }\n}\n\n/**\n * Generate a comprehensive inbox summary\n */\nexport async function summarizeInbox(emails: EmailMessage[]): Promise<InboxSummary> {\n // Parse and categorize all emails\n const parsedEmails = emails.map(parseEmail);\n const categorizations = await Promise.all(\n parsedEmails.slice(0, 100).map(async email => ({\n email,\n categorization: await categorizeEmail(email),\n }))\n );\n\n // Count statistics\n const totalEmails = emails.length;\n const unreadCount = parsedEmails.filter(e => !e.metadata.isRead).length;\n const importantCount = categorizations.filter(\n c => c.categorization.category === \"urgent\" || c.categorization.category === \"action_required\"\n ).length;\n\n // Group by category\n const categoryMap = new Map<string, { emails: ParsedEmail[]; senders: Set<string> }>();\n\n for (const { email, categorization } of categorizations) {\n const cat = categorization.category;\n if (!categoryMap.has(cat)) {\n categoryMap.set(cat, { emails: [], senders: new Set() });\n }\n const entry = categoryMap.get(cat)!;\n entry.emails.push(email);\n if (email.from[0]) {\n entry.senders.add(email.from[0].name || email.from[0].address);\n }\n }\n\n const categories: CategorySummary[] = [];\n for (const [name, data] of categoryMap) {\n categories.push({\n name,\n count: data.emails.length,\n unreadCount: data.emails.filter(e => !e.metadata.isRead).length,\n topSenders: Array.from(data.senders).slice(0, 5),\n description: getCategoryDescription(name),\n });\n }\n\n // Sort categories by count\n categories.sort((a, b) => b.count - a.count);\n\n // Identify urgent items\n const urgentItems: UrgentItem[] = [];\n for (const { email, categorization } of categorizations) {\n if (categorization.category === \"urgent\") {\n urgentItems.push({\n emailId: email.id,\n subject: email.subject,\n from: email.from[0]?.name || email.from[0]?.address || \"Unknown\",\n reason: categorization.reason,\n suggestedAction: \"Review and respond as soon as possible\",\n });\n }\n }\n\n // Extract action items from action_required emails\n const actionItems: ActionItem[] = [];\n const actionRequiredEmails = categorizations\n .filter(c => c.categorization.category === \"action_required\")\n .slice(0, 10);\n\n for (const { email } of actionRequiredEmails) {\n const items = await extractActionItems(email);\n actionItems.push(...items);\n }\n\n // Sort action items by priority\n actionItems.sort((a, b) => {\n const priorityOrder = { high: 0, medium: 1, low: 2 };\n return priorityOrder[a.priority] - priorityOrder[b.priority];\n });\n\n // Generate summary text\n const categoriesText = categories\n .map(c => `- ${c.name}: ${c.count} emails (${c.unreadCount} unread)`)\n .join(\"\\n\");\n\n const urgentText = urgentItems.length > 0\n ? urgentItems.map(u => `- \"${u.subject}\" from ${u.from}`).join(\"\\n\")\n : \"None\";\n\n const summaryPrompt = INBOX_SUMMARY_PROMPT\n .replace(\"{{totalEmails}}\", String(totalEmails))\n .replace(\"{{unreadCount}}\", String(unreadCount))\n .replace(\"{{importantCount}}\", String(importantCount))\n .replace(\"{{categories}}\", categoriesText)\n .replace(\"{{urgentItems}}\", urgentText);\n\n let summary = \"\";\n try {\n const response = await chat(\n [{ role: \"user\", content: summaryPrompt }],\n \"You are a helpful email assistant providing inbox summaries. Be concise and actionable.\"\n );\n summary = response.content;\n } catch (err) {\n console.error(\"Failed to generate summary:\", err);\n summary = `You have ${totalEmails} emails, ${unreadCount} unread. ${importantCount} require attention.`;\n }\n\n return {\n totalEmails,\n unreadCount,\n importantCount,\n categories,\n urgentItems,\n actionItems: actionItems.slice(0, 20),\n summary,\n generatedAt: new Date(),\n };\n}\n\n/**\n * Generate a daily digest summary\n */\nexport async function generateDailyDigest(emails: EmailMessage[]): Promise<string> {\n const summary = await summarizeInbox(emails);\n\n let digest = `# Daily Email Digest\\n\\n`;\n digest += `*Generated: ${summary.generatedAt.toLocaleString()}*\\n\\n`;\n\n // Overview\n digest += `## Overview\\n\\n`;\n digest += `${summary.summary}\\n\\n`;\n digest += `- **Total Emails:** ${summary.totalEmails}\\n`;\n digest += `- **Unread:** ${summary.unreadCount}\\n`;\n digest += `- **Needs Attention:** ${summary.importantCount}\\n\\n`;\n\n // Urgent items\n if (summary.urgentItems.length > 0) {\n digest += `## Urgent\\n\\n`;\n for (const item of summary.urgentItems) {\n digest += `- **${item.subject}** from ${item.from}\\n`;\n digest += ` *${item.reason}*\\n\\n`;\n }\n }\n\n // Action items\n if (summary.actionItems.length > 0) {\n digest += `## Action Items\\n\\n`;\n for (const item of summary.actionItems) {\n const priority = item.priority === \"high\" ? \"!!!\" : item.priority === \"medium\" ? \"!!\" : \"!\";\n digest += `- [${priority}] **${item.action}**\\n`;\n digest += ` From: ${item.from} | ${item.context}\\n`;\n if (item.dueDate) {\n digest += ` Due: ${item.dueDate.toLocaleDateString()}\\n`;\n }\n digest += \"\\n\";\n }\n }\n\n // Categories breakdown\n digest += `## By Category\\n\\n`;\n for (const cat of summary.categories.slice(0, 8)) {\n digest += `- **${cat.name}:** ${cat.count} emails`;\n if (cat.unreadCount > 0) {\n digest += ` (${cat.unreadCount} unread)`;\n }\n digest += \"\\n\";\n if (cat.topSenders.length > 0) {\n digest += ` Top senders: ${cat.topSenders.slice(0, 3).join(\", \")}\\n`;\n }\n }\n\n return digest;\n}\n\n/**\n * Smart reply suggestions for an email\n */\nexport async function suggestReplies(email: EmailMessage | ParsedEmail): Promise<string[]> {\n const parsed = \"metadata\" in email ? email : parseEmail(email);\n\n const fromDisplay = parsed.from[0]\n ? `${parsed.from[0].name || \"\"} <${parsed.from[0].address}>`.trim()\n : \"Unknown\";\n\n const bodyParts = parseEmailBody(parsed.body.text);\n const relevantBody = (bodyParts.newContent || parsed.body.text).substring(0, 1500);\n\n const prompt = `You are an email assistant suggesting reply options. Based on the following email, suggest 3 brief reply options ranging from formal to casual.\n\nEmail:\nFrom: ${fromDisplay}\nSubject: ${parsed.subject}\nBody: ${relevantBody}\n\nProvide 3 reply suggestions:\n1. A brief, professional response\n2. A friendly but complete response\n3. A quick, informal response\n\nRespond in JSON format:\n{\n \"replies\": [\"reply 1\", \"reply 2\", \"reply 3\"]\n}`;\n\n try {\n const response = await chat(\n [{ role: \"user\", content: prompt }],\n \"You are a helpful email assistant. Always respond with valid JSON.\"\n );\n\n const result = JSON.parse(response.content);\n return result.replies || [];\n } catch (err) {\n console.error(\"Failed to suggest replies:\", err);\n return [];\n }\n}\n\n/**\n * Analyze email sentiment\n */\nexport async function analyzeSentiment(email: EmailMessage | ParsedEmail): Promise<{\n sentiment: \"positive\" | \"negative\" | \"neutral\";\n confidence: number;\n tone: string;\n keyPhrases: string[];\n}> {\n const parsed = \"metadata\" in email ? email : parseEmail(email);\n\n const bodyParts = parseEmailBody(parsed.body.text);\n const relevantBody = (bodyParts.newContent || parsed.body.text).substring(0, 1500);\n\n const prompt = `Analyze the sentiment and tone of this email:\n\nSubject: ${parsed.subject}\nBody: ${relevantBody}\n\nRespond in JSON format:\n{\n \"sentiment\": \"positive|negative|neutral\",\n \"confidence\": 0.0-1.0,\n \"tone\": \"brief description of tone (e.g., 'urgent', 'friendly', 'formal', 'frustrated')\",\n \"keyPhrases\": [\"phrase1\", \"phrase2\", \"phrase3\"]\n}`;\n\n try {\n const response = await chat(\n [{ role: \"user\", content: prompt }],\n \"You are a helpful sentiment analysis assistant. Always respond with valid JSON.\"\n );\n\n const result = JSON.parse(response.content);\n return {\n sentiment: result.sentiment || \"neutral\",\n confidence: result.confidence || 0.5,\n tone: result.tone || \"neutral\",\n keyPhrases: result.keyPhrases || [],\n };\n } catch (err) {\n console.error(\"Failed to analyze sentiment:\", err);\n return {\n sentiment: \"neutral\",\n confidence: 0.5,\n tone: \"neutral\",\n keyPhrases: [],\n };\n }\n}\n\n/**\n * Get description for a category\n */\nfunction getCategoryDescription(category: string): string {\n const descriptions: Record<string, string> = {\n urgent: \"Time-sensitive emails requiring immediate attention\",\n action_required: \"Emails that need your response or action\",\n informational: \"FYI emails and updates\",\n meeting: \"Calendar invites and meeting-related emails\",\n financial: \"Invoices, receipts, and financial communications\",\n social: \"Personal and social notifications\",\n marketing: \"Promotional emails and newsletters\",\n support: \"Customer support and help desk communications\",\n work: \"Work-related emails\",\n spam: \"Potentially unwanted emails\",\n };\n return descriptions[category] || \"Other emails\";\n}\n\nexport default {\n categorizeEmail,\n extractActionItems,\n summarizeThread,\n summarizeInbox,\n generateDailyDigest,\n suggestReplies,\n analyzeSentiment,\n};\n"],"mappings":"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","names":[]}
|