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
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/inputs/api/server.ts","../src/core/security/api-key-manager.ts","../src/core/security/session-manager.ts","../src/core/security/gateway-utils.ts","../src/core/security/auth-middleware.ts","../src/inputs/api/routes/osint.ts","../src/inputs/api/routes/email.ts","../src/inputs/api/routes/sdk.ts","../src/inputs/api/routes/admin.ts","../src/core/security/rate-limiter.ts"],"sourcesContent":["import { Hono } from \"hono\";\r\nimport { cors } from \"hono/cors\";\r\nimport { logger } from \"hono/logger\";\r\n// Use platform-appropriate static file serving\r\nlet serveStatic: any;\r\ntry {\r\n // Bun runtime\r\n serveStatic = require(\"hono/bun\").serveStatic;\r\n} catch {\r\n // Fallback: no static file serving (API-only mode)\r\n serveStatic = () => async (_c: any, next: any) => next();\r\n}\r\nimport { chat, chatWithTools, streamChat, type Message } from \"../../core/brain\";\r\nimport { transcribeAudio } from \"../../outputs/stt\";\r\nimport { textToSpeech } from \"../../outputs/tts\";\r\nimport { db, conversations, messages, memories } from \"../../db\";\r\nimport { desc, eq } from \"drizzle-orm\";\r\nimport { searchMemories, storeMemory, updateMemory, deleteMemory, exportMemories, getMemoryById } from \"../../core/memory\";\r\nimport { authMiddleware, requirePermission, getAuthUserId } from \"../../core/security/auth-middleware\";\r\nimport { decryptField } from \"../../core/security/field-encryption\";\r\n\r\nconst app = new Hono();\r\n\r\n// Middleware\r\napp.use(\"*\", logger());\r\napp.use(\"/api/*\", cors());\r\napp.use(\"/api/*\", authMiddleware());\r\n\r\n// Health check\r\napp.get(\"/health\", (c) => {\r\n return c.json({ status: \"ok\", timestamp: new Date().toISOString() });\r\n});\r\n\r\n// ===== Chat API =====\r\n\r\n// Simple chat endpoint (no tools)\r\napp.post(\"/api/chat\", async (c) => {\r\n try {\r\n const body = await c.req.json<{\r\n messages: Message[];\r\n systemPrompt?: string;\r\n }>();\r\n\r\n if (!body.messages || !Array.isArray(body.messages)) {\r\n return c.json({ error: \"messages array is required\" }, 400);\r\n }\r\n\r\n const response = await chat(body.messages, body.systemPrompt);\r\n\r\n return c.json({\r\n content: response.content,\r\n usage: {\r\n inputTokens: response.inputTokens,\r\n outputTokens: response.outputTokens,\r\n },\r\n });\r\n } catch (error) {\r\n console.error(\"Chat API error:\", error);\r\n return c.json({ error: \"Internal server error\" }, 500);\r\n }\r\n});\r\n\r\n// Chat with tools endpoint\r\napp.post(\"/api/chat/tools\", requirePermission(\"chat:tools\" as any), async (c) => {\r\n try {\r\n const body = await c.req.json<{\r\n messages: Message[];\r\n userId?: string;\r\n }>();\r\n\r\n if (!body.messages || !Array.isArray(body.messages)) {\r\n return c.json({ error: \"messages array is required\" }, 400);\r\n }\r\n\r\n const toolsUsed: string[] = [];\r\n const response = await chatWithTools(body.messages, body.userId, (tool) => {\r\n toolsUsed.push(tool);\r\n });\r\n\r\n return c.json({\r\n content: response.content,\r\n toolsUsed,\r\n usage: {\r\n inputTokens: response.inputTokens,\r\n outputTokens: response.outputTokens,\r\n },\r\n });\r\n } catch (error) {\r\n console.error(\"Chat with tools API error:\", error);\r\n return c.json({ error: \"Internal server error\" }, 500);\r\n }\r\n});\r\n\r\n// Simple single message chat\r\napp.post(\"/api/ask\", async (c) => {\r\n try {\r\n const body = await c.req.json<{\r\n message: string;\r\n systemPrompt?: string;\r\n useTools?: boolean;\r\n }>();\r\n\r\n if (!body.message) {\r\n return c.json({ error: \"message is required\" }, 400);\r\n }\r\n\r\n const messages: Message[] = [{ role: \"user\", content: body.message }];\r\n\r\n const response = body.useTools\r\n ? await chatWithTools(messages)\r\n : await chat(messages, body.systemPrompt);\r\n\r\n return c.json({\r\n content: response.content,\r\n toolsUsed: response.toolsUsed,\r\n usage: {\r\n inputTokens: response.inputTokens,\r\n outputTokens: response.outputTokens,\r\n },\r\n });\r\n } catch (error) {\r\n console.error(\"Ask API error:\", error);\r\n return c.json({ error: \"Internal server error\" }, 500);\r\n }\r\n});\r\n\r\n// ===== Conversations API =====\r\n\r\napp.get(\"/api/conversations\", async (c) => {\r\n try {\r\n const convos = await db\r\n .select()\r\n .from(conversations)\r\n .orderBy(desc(conversations.updatedAt))\r\n .limit(50);\r\n\r\n return c.json(convos);\r\n } catch (error) {\r\n console.error(\"Error fetching conversations:\", error);\r\n return c.json({ error: \"Internal server error\" }, 500);\r\n }\r\n});\r\n\r\napp.get(\"/api/conversations/:id\", async (c) => {\r\n try {\r\n const id = c.req.param(\"id\");\r\n const convo = await db\r\n .select()\r\n .from(conversations)\r\n .where(eq(conversations.id, id))\r\n .limit(1);\r\n\r\n if (convo.length === 0) {\r\n return c.json({ error: \"Conversation not found\" }, 404);\r\n }\r\n\r\n const msgs = await db\r\n .select()\r\n .from(messages)\r\n .where(eq(messages.conversationId, id))\r\n .orderBy(messages.createdAt);\r\n\r\n // Decrypt encrypted messages\r\n const decryptedMsgs = msgs.map((m) => {\r\n if ((m as any).encrypted && m.content) {\r\n try {\r\n return { ...m, content: decryptField(m.content) ?? m.content };\r\n } catch {\r\n return m;\r\n }\r\n }\r\n return m;\r\n });\r\n\r\n return c.json({ conversation: convo[0], messages: decryptedMsgs });\r\n } catch (error) {\r\n console.error(\"Error fetching conversation:\", error);\r\n return c.json({ error: \"Internal server error\" }, 500);\r\n }\r\n});\r\n\r\n// ===== Memories API =====\r\n\r\napp.get(\"/api/memories\", async (c) => {\r\n try {\r\n const userId = c.req.query(\"userId\");\r\n const limit = parseInt(c.req.query(\"limit\") || \"50\");\r\n\r\n let query = db.select().from(memories).orderBy(desc(memories.createdAt)).limit(limit);\r\n\r\n const mems = await query;\r\n return c.json(mems);\r\n } catch (error) {\r\n console.error(\"Error fetching memories:\", error);\r\n return c.json({ error: \"Internal server error\" }, 500);\r\n }\r\n});\r\n\r\napp.post(\"/api/memories/search\", async (c) => {\r\n try {\r\n const body = await c.req.json<{ query: string; userId?: string; limit?: number }>();\r\n\r\n if (!body.query) {\r\n return c.json({ error: \"query is required\" }, 400);\r\n }\r\n\r\n const results = await searchMemories(body.query, body.userId, body.limit || 5);\r\n return c.json(results);\r\n } catch (error) {\r\n console.error(\"Error searching memories:\", error);\r\n return c.json({ error: \"Internal server error\" }, 500);\r\n }\r\n});\r\n\r\napp.post(\"/api/memories\", async (c) => {\r\n try {\r\n const body = await c.req.json<{\r\n content: string;\r\n type?: \"episodic\" | \"semantic\" | \"procedural\";\r\n importance?: number;\r\n userId?: string;\r\n }>();\r\n\r\n if (!body.content) {\r\n return c.json({ error: \"content is required\" }, 400);\r\n }\r\n\r\n const memory = await storeMemory({\r\n content: body.content,\r\n type: body.type || \"semantic\",\r\n importance: body.importance || 5,\r\n userId: body.userId,\r\n source: \"api\",\r\n provenance: \"api:manual\",\r\n });\r\n\r\n return c.json(memory);\r\n } catch (error) {\r\n console.error(\"Error storing memory:\", error);\r\n return c.json({ error: \"Internal server error\" }, 500);\r\n }\r\n});\r\n\r\n// Get a single memory by ID\r\napp.get(\"/api/memories/export\", async (c) => {\r\n try {\r\n const userId = c.req.query(\"userId\");\r\n const format = (c.req.query(\"format\") || \"markdown\") as \"markdown\" | \"json\";\r\n\r\n const exported = await exportMemories(userId || undefined, format);\r\n\r\n if (format === \"json\") {\r\n return c.json(JSON.parse(exported));\r\n }\r\n\r\n return c.text(exported);\r\n } catch (error) {\r\n console.error(\"Error exporting memories:\", error);\r\n return c.json({ error: \"Internal server error\" }, 500);\r\n }\r\n});\r\n\r\napp.get(\"/api/memories/:id\", async (c) => {\r\n try {\r\n const id = c.req.param(\"id\");\r\n const memory = await getMemoryById(id);\r\n\r\n if (!memory) {\r\n return c.json({ error: \"Memory not found\" }, 404);\r\n }\r\n\r\n return c.json(memory);\r\n } catch (error) {\r\n console.error(\"Error fetching memory:\", error);\r\n return c.json({ error: \"Internal server error\" }, 500);\r\n }\r\n});\r\n\r\n// Update a memory\r\napp.put(\"/api/memories/:id\", async (c) => {\r\n try {\r\n const id = c.req.param(\"id\");\r\n const body = await c.req.json<{\r\n content?: string;\r\n type?: string;\r\n importance?: number;\r\n }>();\r\n\r\n const updated = await updateMemory(id, body);\r\n\r\n if (!updated) {\r\n return c.json({ error: \"Memory not found or no changes\" }, 404);\r\n }\r\n\r\n return c.json(updated);\r\n } catch (error) {\r\n console.error(\"Error updating memory:\", error);\r\n return c.json({ error: \"Internal server error\" }, 500);\r\n }\r\n});\r\n\r\n// Delete a memory (soft-delete to archive)\r\napp.delete(\"/api/memories/:id\", requirePermission(\"memories:delete\" as any), async (c) => {\r\n try {\r\n const id = c.req.param(\"id\");\r\n const deleted = await deleteMemory(id);\r\n\r\n if (!deleted) {\r\n return c.json({ error: \"Memory not found\" }, 404);\r\n }\r\n\r\n return c.json({ success: true, id });\r\n } catch (error) {\r\n console.error(\"Error deleting memory:\", error);\r\n return c.json({ error: \"Internal server error\" }, 500);\r\n }\r\n});\r\n\r\n// ===== Autonomy API =====\r\n\r\napp.get(\"/api/autonomy\", async (c) => {\r\n try {\r\n const { autonomyManager } = await import(\"../../core/security/autonomy\");\r\n const userId = c.req.query(\"userId\");\r\n return c.json({\r\n level: userId ? autonomyManager.getLevel(userId) : autonomyManager.getDefaultLevel(),\r\n stats: autonomyManager.getStats(),\r\n });\r\n } catch (error) {\r\n return c.json({ error: \"Autonomy system not available\" }, 500);\r\n }\r\n});\r\n\r\napp.put(\"/api/autonomy\", async (c) => {\r\n try {\r\n const { autonomyManager } = await import(\"../../core/security/autonomy\");\r\n const body = await c.req.json<{ level: string; userId?: string }>();\r\n const level = body.level as \"readonly\" | \"supervised\" | \"autonomous\";\r\n\r\n if (![\"readonly\", \"supervised\", \"autonomous\"].includes(level)) {\r\n return c.json({ error: \"Invalid level. Must be: readonly, supervised, or autonomous\" }, 400);\r\n }\r\n\r\n if (body.userId) {\r\n autonomyManager.setLevel(body.userId, level);\r\n } else {\r\n autonomyManager.setDefaultLevel(level);\r\n }\r\n\r\n return c.json({ success: true, level });\r\n } catch (error) {\r\n return c.json({ error: \"Autonomy system not available\" }, 500);\r\n }\r\n});\r\n\r\n// ===== Prometheus Metrics =====\r\n\r\napp.get(\"/metrics\", async (c) => {\r\n try {\r\n const { prometheusExporter } = await import(\"../../core/observability/prometheus\");\r\n const text = prometheusExporter.toTextFormat();\r\n return c.text(text, 200, { \"Content-Type\": \"text/plain; version=0.0.4; charset=utf-8\" });\r\n } catch (error) {\r\n return c.text(\"# Prometheus metrics not available\\n\", 500);\r\n }\r\n});\r\n\r\napp.get(\"/api/metrics/prometheus\", async (c) => {\r\n try {\r\n const { prometheusExporter } = await import(\"../../core/observability/prometheus\");\r\n const text = prometheusExporter.toTextFormat();\r\n return c.text(text, 200, { \"Content-Type\": \"text/plain; version=0.0.4; charset=utf-8\" });\r\n } catch (error) {\r\n return c.text(\"# Prometheus metrics not available\\n\", 500);\r\n }\r\n});\r\n\r\n// ===== Pairing API =====\r\n\r\napp.post(\"/api/pair\", async (c) => {\r\n try {\r\n const { pairingManager } = await import(\"../../core/security/pairing\");\r\n const body = await c.req.json<{ code: string; deviceInfo?: string }>();\r\n\r\n if (!body.code) {\r\n return c.json({ error: \"code is required\" }, 400);\r\n }\r\n\r\n const result = pairingManager.pair(body.code, body.deviceInfo || \"Unknown device\");\r\n\r\n if (!result.success) {\r\n return c.json({ error: result.error }, 401);\r\n }\r\n\r\n return c.json({ token: result.token });\r\n } catch (error) {\r\n return c.json({ error: \"Pairing system not available\" }, 500);\r\n }\r\n});\r\n\r\n// ===== Providers API =====\r\n\r\napp.get(\"/api/providers\", async (c) => {\r\n try {\r\n const { providerRegistry } = await import(\"../../core/providers\");\r\n return c.json({\r\n providers: providerRegistry.listProviders(),\r\n default: providerRegistry.getDefaultId(),\r\n });\r\n } catch (error) {\r\n return c.json({ error: \"Provider system not available\" }, 500);\r\n }\r\n});\r\n\r\n// ===== OSINT API =====\r\nimport { osintRoutes } from \"./routes/osint\";\r\napp.route(\"/api/osint\", osintRoutes);\r\n\r\nimport { emailRoutes } from \"./routes/email\";\r\napp.route(\"/api/email\", emailRoutes);\r\n\r\n// ===== SDK API (External App Integration) =====\r\nimport { sdkRoutes } from \"./routes/sdk\";\r\napp.route(\"/api/sdk\", sdkRoutes);\r\n\r\n// ===== Admin API (Audit Logs, Incidents) =====\r\nimport adminRouter from \"./routes/admin\";\r\napp.route(\"/api/admin\", adminRouter);\r\n\r\n// ===== Incident Response API =====\r\n\r\napp.get(\"/api/incidents\", requirePermission(\"admin:settings\" as any), async (c) => {\r\n try {\r\n const { getOpenIncidents } = await import(\"../../core/security/incident-response\");\r\n const severity = c.req.query(\"severity\") as any;\r\n const type = c.req.query(\"type\") as any;\r\n const incidents = await getOpenIncidents({ severity, type, limit: 50 });\r\n return c.json(incidents);\r\n } catch (error) {\r\n return c.json({ error: \"Incident system not available\" }, 500);\r\n }\r\n});\r\n\r\napp.get(\"/api/incidents/:id\", requirePermission(\"admin:settings\" as any), async (c) => {\r\n try {\r\n const { generateIncidentReport } = await import(\"../../core/security/incident-response\");\r\n const id = c.req.param(\"id\");\r\n const report = await generateIncidentReport(id);\r\n return c.json(report);\r\n } catch (error) {\r\n return c.json({ error: \"Incident not found\" }, 404);\r\n }\r\n});\r\n\r\napp.post(\"/api/incidents/:id/status\", requirePermission(\"admin:settings\" as any), async (c) => {\r\n try {\r\n const { updateIncidentStatus } = await import(\"../../core/security/incident-response\");\r\n const id = c.req.param(\"id\");\r\n const body = await c.req.json<{ status: string; notes?: string }>();\r\n const userId = getAuthUserId(c);\r\n const updated = await updateIncidentStatus(id, body.status as any, userId, body.notes);\r\n return c.json(updated);\r\n } catch (error) {\r\n return c.json({ error: \"Failed to update incident\" }, 500);\r\n }\r\n});\r\n\r\napp.post(\"/api/incidents/:id/resolve\", requirePermission(\"admin:settings\" as any), async (c) => {\r\n try {\r\n const { resolveIncident } = await import(\"../../core/security/incident-response\");\r\n const id = c.req.param(\"id\");\r\n const body = await c.req.json<{ notes: string }>();\r\n const userId = getAuthUserId(c);\r\n const resolved = await resolveIncident(id, body.notes, userId);\r\n return c.json(resolved);\r\n } catch (error) {\r\n return c.json({ error: \"Failed to resolve incident\" }, 500);\r\n }\r\n});\r\n\r\n// ===== Audit Chain Integrity API =====\r\n\r\napp.get(\"/api/audit/integrity\", requirePermission(\"admin:settings\" as any), async (c) => {\r\n try {\r\n const { getAuditChainIntegrity } = await import(\"../../core/security/audit-logger\");\r\n const integrity = await getAuditChainIntegrity();\r\n return c.json(integrity);\r\n } catch (error) {\r\n return c.json({ error: \"Audit system not available\" }, 500);\r\n }\r\n});\r\n\r\n// ===== Voice API (STT / TTS) =====\r\n\r\napp.post(\"/api/transcribe\", async (c) => {\r\n try {\r\n const formData = await c.req.formData();\r\n const audioFile = formData.get(\"audio\");\r\n\r\n if (!audioFile || !(audioFile instanceof File)) {\r\n return c.json({ error: \"audio file is required\" }, 400);\r\n }\r\n\r\n const buffer = Buffer.from(await audioFile.arrayBuffer());\r\n const text = await transcribeAudio(buffer);\r\n\r\n if (!text) {\r\n return c.json({ error: \"Could not transcribe audio\" }, 500);\r\n }\r\n\r\n return c.json({ text });\r\n } catch (error) {\r\n console.error(\"Transcribe API error:\", error);\r\n return c.json({ error: \"Transcription failed\" }, 500);\r\n }\r\n});\r\n\r\napp.post(\"/api/tts\", async (c) => {\r\n try {\r\n const body = await c.req.json<{ text: string }>();\r\n\r\n if (!body.text) {\r\n return c.json({ error: \"text is required\" }, 400);\r\n }\r\n\r\n const audioBuffer = await textToSpeech(body.text);\r\n\r\n if (!audioBuffer) {\r\n return c.json({ error: \"Text-to-speech failed\" }, 500);\r\n }\r\n\r\n return new Response(new Uint8Array(audioBuffer), {\r\n headers: {\r\n \"Content-Type\": \"audio/mpeg\",\r\n \"Content-Length\": String(audioBuffer.length),\r\n },\r\n });\r\n } catch (error) {\r\n console.error(\"TTS API error:\", error);\r\n return c.json({ error: \"TTS failed\" }, 500);\r\n }\r\n});\r\n\r\n// ===== System API =====\r\n\r\napp.get(\"/api/system/status\", async (c) => {\r\n // Only expose detailed stats to authenticated requests (Bearer token)\r\n const authHeader = c.req.header(\"Authorization\");\r\n if (authHeader?.startsWith(\"Bearer \")) {\r\n return c.json({\r\n status: \"online\",\r\n version: \"3.0.0\",\r\n uptime: process.uptime(),\r\n memory: process.memoryUsage(),\r\n });\r\n }\r\n // Public: only expose status and version (no runtime details)\r\n return c.json({\r\n status: \"online\",\r\n version: \"3.0.0\",\r\n });\r\n});\r\n\r\n// ===== Spotify OAuth Callback =====\r\n\r\napp.get(\"/api/callbacks/spotify\", async (c) => {\r\n try {\r\n const code = c.req.query(\"code\");\r\n const error = c.req.query(\"error\");\r\n\r\n if (error) {\r\n return c.html(`<h1>Spotify Authorization Failed</h1><p>Error: ${error}</p><p>Go back and try again.</p>`);\r\n }\r\n\r\n if (!code) {\r\n return c.html(`<h1>Missing Authorization Code</h1><p>No code received from Spotify.</p>`);\r\n }\r\n\r\n const { env: appEnv } = await import(\"../../config/env\");\r\n if (!appEnv.SPOTIFY_CLIENT_ID || !appEnv.SPOTIFY_CLIENT_SECRET) {\r\n return c.html(`<h1>Spotify Not Configured</h1><p>Set SPOTIFY_CLIENT_ID and SPOTIFY_CLIENT_SECRET in .env</p>`);\r\n }\r\n\r\n const { createSpotifyAuth } = await import(\"../../integrations/spotify/auth\");\r\n const auth = createSpotifyAuth({\r\n clientId: appEnv.SPOTIFY_CLIENT_ID,\r\n clientSecret: appEnv.SPOTIFY_CLIENT_SECRET,\r\n redirectUri: appEnv.SPOTIFY_REDIRECT_URI || `http://localhost:${appEnv.PORT}/api/callbacks/spotify`,\r\n });\r\n\r\n const tokens = await auth.exchangeCode(code);\r\n\r\n return c.html(`\r\n <html><body style=\"font-family: system-ui; max-width: 600px; margin: 40px auto; padding: 20px;\">\r\n <h1>Spotify Connected!</h1>\r\n <p>Add this to your <code>.env</code> file:</p>\r\n <pre style=\"background: #f0f0f0; padding: 16px; border-radius: 8px; word-break: break-all; white-space: pre-wrap;\">SPOTIFY_REFRESH_TOKEN=${tokens.refreshToken}</pre>\r\n <p>Then restart OpenSentinel. You can close this page.</p>\r\n </body></html>\r\n `);\r\n } catch (err) {\r\n console.error(\"Spotify callback error:\", err);\r\n return c.html(`<h1>Spotify Auth Error</h1><p>${err instanceof Error ? err.message : String(err)}</p>`);\r\n }\r\n});\r\n\r\napp.get(\"/api/spotify/authorize\", async (c) => {\r\n try {\r\n const { env: appEnv } = await import(\"../../config/env\");\r\n if (!appEnv.SPOTIFY_CLIENT_ID || !appEnv.SPOTIFY_CLIENT_SECRET) {\r\n return c.json({ error: \"Spotify not configured. Set SPOTIFY_CLIENT_ID and SPOTIFY_CLIENT_SECRET in .env\" }, 400);\r\n }\r\n\r\n const { createSpotifyAuth, DEFAULT_SCOPES } = await import(\"../../integrations/spotify/auth\");\r\n const auth = createSpotifyAuth({\r\n clientId: appEnv.SPOTIFY_CLIENT_ID,\r\n clientSecret: appEnv.SPOTIFY_CLIENT_SECRET,\r\n redirectUri: appEnv.SPOTIFY_REDIRECT_URI || `http://localhost:${appEnv.PORT}/api/callbacks/spotify`,\r\n });\r\n\r\n const url = auth.getAuthorizationUrl(DEFAULT_SCOPES, undefined, true);\r\n return c.json({ url });\r\n } catch (err) {\r\n return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);\r\n }\r\n});\r\n\r\n// Serve static files from web/dist\r\napp.use(\"/*\", serveStatic({ root: \"./src/web/dist\" }));\r\n\r\n// Fallback to index.html for SPA routing\r\napp.get(\"/*\", serveStatic({ path: \"./src/web/dist/index.html\" }));\r\n\r\nexport { app };\r\n","import { db } from \"../../db\";\nimport { apiKeys, NewApiKey } from \"../../db/schema\";\nimport { eq, and, isNull } from \"drizzle-orm\";\nimport { randomBytes, createHash } from \"crypto\";\n\nconst KEY_PREFIX = \"mb_\";\nconst KEY_LENGTH = 32;\n\nexport type Permission =\n | \"chat:basic\"\n | \"chat:tools\"\n | \"tools:shell\"\n | \"tools:files\"\n | \"tools:browser\"\n | \"tools:web_search\"\n | \"agents:spawn\"\n | \"agents:manage\"\n | \"memories:read\"\n | \"memories:write\"\n | \"memories:delete\"\n | \"admin:users\"\n | \"admin:settings\"\n | \"*\"; // All permissions\n\nexport const PERMISSION_GROUPS: Record<string, Permission[]> = {\n readonly: [\"chat:basic\", \"memories:read\"],\n standard: [\n \"chat:basic\",\n \"chat:tools\",\n \"tools:files\",\n \"tools:web_search\",\n \"memories:read\",\n \"memories:write\",\n ],\n full: [\n \"chat:basic\",\n \"chat:tools\",\n \"tools:shell\",\n \"tools:files\",\n \"tools:browser\",\n \"tools:web_search\",\n \"agents:spawn\",\n \"agents:manage\",\n \"memories:read\",\n \"memories:write\",\n \"memories:delete\",\n ],\n admin: [\"*\"],\n};\n\nexport interface ApiKeyInfo {\n id: string;\n name: string;\n prefix: string;\n permissions: Permission[];\n createdAt: Date;\n lastUsedAt: Date | null;\n expiresAt: Date | null;\n isRevoked: boolean;\n}\n\nexport interface CreateApiKeyOptions {\n userId: string;\n name: string;\n permissions?: Permission[];\n permissionGroup?: keyof typeof PERMISSION_GROUPS;\n expiresInDays?: number;\n}\n\nfunction generateApiKey(): string {\n const random = randomBytes(KEY_LENGTH).toString(\"base64url\");\n return `${KEY_PREFIX}${random}`;\n}\n\nfunction hashApiKey(key: string): string {\n return createHash(\"sha256\").update(key).digest(\"hex\");\n}\n\nfunction getKeyPrefix(key: string): string {\n // Return first 12 characters including the prefix\n return key.slice(0, 12);\n}\n\nexport async function createApiKey(\n options: CreateApiKeyOptions\n): Promise<{ apiKey: ApiKeyInfo; rawKey: string }> {\n const {\n userId,\n name,\n permissions,\n permissionGroup,\n expiresInDays,\n } = options;\n\n // Determine permissions\n let finalPermissions: Permission[];\n if (permissions) {\n finalPermissions = permissions;\n } else if (permissionGroup) {\n finalPermissions = PERMISSION_GROUPS[permissionGroup] || PERMISSION_GROUPS.standard;\n } else {\n finalPermissions = PERMISSION_GROUPS.standard;\n }\n\n const rawKey = generateApiKey();\n const keyHash = hashApiKey(rawKey);\n const keyPrefix = getKeyPrefix(rawKey);\n\n const expiresAt = expiresInDays\n ? new Date(Date.now() + expiresInDays * 24 * 60 * 60 * 1000)\n : null;\n\n const [created] = await db\n .insert(apiKeys)\n .values({\n userId,\n name,\n keyHash,\n keyPrefix,\n permissions: finalPermissions as string[],\n expiresAt,\n })\n .returning();\n\n return {\n apiKey: {\n id: created.id,\n name: created.name,\n prefix: created.keyPrefix,\n permissions: (created.permissions as Permission[]) || [],\n createdAt: created.createdAt,\n lastUsedAt: created.lastUsedAt,\n expiresAt: created.expiresAt,\n isRevoked: !!created.revokedAt,\n },\n rawKey, // Only returned once during creation\n };\n}\n\nexport async function validateApiKey(\n rawKey: string\n): Promise<{ valid: boolean; apiKey?: ApiKeyInfo; userId?: string }> {\n if (!rawKey.startsWith(KEY_PREFIX)) {\n return { valid: false };\n }\n\n const keyHash = hashApiKey(rawKey);\n const now = new Date();\n\n const [key] = await db\n .select()\n .from(apiKeys)\n .where(\n and(\n eq(apiKeys.keyHash, keyHash),\n isNull(apiKeys.revokedAt)\n )\n )\n .limit(1);\n\n if (!key) {\n return { valid: false };\n }\n\n // Check expiration\n if (key.expiresAt && key.expiresAt < now) {\n return { valid: false };\n }\n\n // Update last used timestamp\n await db\n .update(apiKeys)\n .set({ lastUsedAt: now })\n .where(eq(apiKeys.id, key.id));\n\n return {\n valid: true,\n apiKey: {\n id: key.id,\n name: key.name,\n prefix: key.keyPrefix,\n permissions: (key.permissions as Permission[]) || [],\n createdAt: key.createdAt,\n lastUsedAt: now,\n expiresAt: key.expiresAt,\n isRevoked: false,\n },\n userId: key.userId,\n };\n}\n\nexport function hasPermission(\n apiKeyPermissions: Permission[],\n requiredPermission: Permission\n): boolean {\n // Wildcard grants all permissions\n if (apiKeyPermissions.includes(\"*\")) {\n return true;\n }\n\n return apiKeyPermissions.includes(requiredPermission);\n}\n\nexport function hasAnyPermission(\n apiKeyPermissions: Permission[],\n requiredPermissions: Permission[]\n): boolean {\n return requiredPermissions.some((p) => hasPermission(apiKeyPermissions, p));\n}\n\nexport function hasAllPermissions(\n apiKeyPermissions: Permission[],\n requiredPermissions: Permission[]\n): boolean {\n return requiredPermissions.every((p) => hasPermission(apiKeyPermissions, p));\n}\n\nexport async function revokeApiKey(keyId: string): Promise<boolean> {\n const [revoked] = await db\n .update(apiKeys)\n .set({ revokedAt: new Date() })\n .where(eq(apiKeys.id, keyId))\n .returning();\n\n return !!revoked;\n}\n\nexport async function getUserApiKeys(userId: string): Promise<ApiKeyInfo[]> {\n const keys = await db\n .select()\n .from(apiKeys)\n .where(eq(apiKeys.userId, userId))\n .orderBy(apiKeys.createdAt);\n\n return keys.map((k) => ({\n id: k.id,\n name: k.name,\n prefix: k.keyPrefix,\n permissions: (k.permissions as Permission[]) || [],\n createdAt: k.createdAt,\n lastUsedAt: k.lastUsedAt,\n expiresAt: k.expiresAt,\n isRevoked: !!k.revokedAt,\n }));\n}\n\nexport async function rotateApiKey(\n keyId: string\n): Promise<{ apiKey: ApiKeyInfo; rawKey: string } | null> {\n // Get existing key info\n const [existing] = await db\n .select()\n .from(apiKeys)\n .where(eq(apiKeys.id, keyId))\n .limit(1);\n\n if (!existing || existing.revokedAt) {\n return null;\n }\n\n // Revoke old key\n await revokeApiKey(keyId);\n\n // Create new key with same settings\n return createApiKey({\n userId: existing.userId,\n name: existing.name,\n permissions: existing.permissions as Permission[],\n expiresInDays: existing.expiresAt\n ? Math.ceil((existing.expiresAt.getTime() - Date.now()) / (24 * 60 * 60 * 1000))\n : undefined,\n });\n}\n\n// Delete permanently (use revoke in most cases)\nexport async function deleteApiKey(keyId: string): Promise<boolean> {\n await db.delete(apiKeys).where(eq(apiKeys.id, keyId));\n return true;\n}\n","import { db } from \"../../db\";\nimport { sessions, users } from \"../../db/schema\";\nimport { eq, and, gt, lt } from \"drizzle-orm\";\nimport { randomBytes, createHash } from \"crypto\";\n\nconst SESSION_DURATION_MS = 7 * 24 * 60 * 60 * 1000; // 7 days\nconst TOKEN_LENGTH = 32;\n\nexport interface SessionInfo {\n userId: string;\n sessionId: string;\n expiresAt: Date;\n deviceInfo?: {\n userAgent?: string;\n platform?: string;\n browser?: string;\n };\n}\n\nexport interface CreateSessionOptions {\n userId: string;\n deviceInfo?: {\n userAgent?: string;\n platform?: string;\n browser?: string;\n };\n ipAddress?: string;\n durationMs?: number;\n}\n\nfunction generateToken(): string {\n return randomBytes(TOKEN_LENGTH).toString(\"hex\");\n}\n\nfunction hashToken(token: string): string {\n return createHash(\"sha256\").update(token).digest(\"hex\");\n}\n\nexport async function createSession(\n options: CreateSessionOptions\n): Promise<{ session: SessionInfo; token: string }> {\n const { userId, deviceInfo, ipAddress, durationMs = SESSION_DURATION_MS } = options;\n\n const token = generateToken();\n const tokenHash = hashToken(token);\n const expiresAt = new Date(Date.now() + durationMs);\n\n const [session] = await db\n .insert(sessions)\n .values({\n userId,\n token: tokenHash,\n deviceInfo,\n ipAddress,\n expiresAt,\n })\n .returning();\n\n return {\n session: {\n userId: session.userId,\n sessionId: session.id,\n expiresAt: session.expiresAt,\n deviceInfo: session.deviceInfo as SessionInfo[\"deviceInfo\"],\n },\n token, // Return the raw token to the client (only time it's available)\n };\n}\n\nexport async function validateSession(token: string): Promise<SessionInfo | null> {\n const tokenHash = hashToken(token);\n const now = new Date();\n\n const [session] = await db\n .select()\n .from(sessions)\n .where(and(eq(sessions.token, tokenHash), gt(sessions.expiresAt, now)))\n .limit(1);\n\n if (!session) {\n return null;\n }\n\n // Update last active time\n await db\n .update(sessions)\n .set({ lastActiveAt: now })\n .where(eq(sessions.id, session.id));\n\n return {\n userId: session.userId,\n sessionId: session.id,\n expiresAt: session.expiresAt,\n deviceInfo: session.deviceInfo as SessionInfo[\"deviceInfo\"],\n };\n}\n\nexport async function invalidateSession(sessionId: string): Promise<boolean> {\n const result = await db.delete(sessions).where(eq(sessions.id, sessionId));\n return true;\n}\n\nexport async function invalidateAllUserSessions(userId: string): Promise<number> {\n const result = await db.delete(sessions).where(eq(sessions.userId, userId));\n return 0; // Drizzle doesn't return affected rows easily\n}\n\nexport async function getUserSessions(userId: string): Promise<SessionInfo[]> {\n const now = new Date();\n\n const userSessions = await db\n .select()\n .from(sessions)\n .where(and(eq(sessions.userId, userId), gt(sessions.expiresAt, now)))\n .orderBy(sessions.lastActiveAt);\n\n return userSessions.map((s) => ({\n userId: s.userId,\n sessionId: s.id,\n expiresAt: s.expiresAt,\n deviceInfo: s.deviceInfo as SessionInfo[\"deviceInfo\"],\n }));\n}\n\nexport async function refreshSession(\n sessionId: string,\n durationMs = SESSION_DURATION_MS\n): Promise<SessionInfo | null> {\n const newExpiresAt = new Date(Date.now() + durationMs);\n\n const [updated] = await db\n .update(sessions)\n .set({\n expiresAt: newExpiresAt,\n lastActiveAt: new Date(),\n })\n .where(eq(sessions.id, sessionId))\n .returning();\n\n if (!updated) {\n return null;\n }\n\n return {\n userId: updated.userId,\n sessionId: updated.id,\n expiresAt: updated.expiresAt,\n deviceInfo: updated.deviceInfo as SessionInfo[\"deviceInfo\"],\n };\n}\n\nexport async function cleanupExpiredSessions(): Promise<number> {\n const now = new Date();\n await db.delete(sessions).where(lt(sessions.expiresAt, now));\n return 0; // Cleanup completed\n}\n\n// Parse user agent string into device info\nexport function parseUserAgent(userAgent?: string): SessionInfo[\"deviceInfo\"] {\n if (!userAgent) return undefined;\n\n const info: SessionInfo[\"deviceInfo\"] = { userAgent };\n\n // Simple platform detection\n if (userAgent.includes(\"Windows\")) {\n info.platform = \"Windows\";\n } else if (userAgent.includes(\"Mac\")) {\n info.platform = \"macOS\";\n } else if (userAgent.includes(\"Linux\")) {\n info.platform = \"Linux\";\n } else if (userAgent.includes(\"Android\")) {\n info.platform = \"Android\";\n } else if (userAgent.includes(\"iPhone\") || userAgent.includes(\"iPad\")) {\n info.platform = \"iOS\";\n }\n\n // Simple browser detection\n if (userAgent.includes(\"Firefox\")) {\n info.browser = \"Firefox\";\n } else if (userAgent.includes(\"Chrome\")) {\n info.browser = \"Chrome\";\n } else if (userAgent.includes(\"Safari\")) {\n info.browser = \"Safari\";\n } else if (userAgent.includes(\"Edge\")) {\n info.browser = \"Edge\";\n }\n\n return info;\n}\n","/**\n * Gateway Token Utilities\n *\n * Shared helpers for gateway token authentication (OpenClaw-style).\n * Used by both the HTTP auth middleware and the WebSocket upgrade handler.\n */\n\nimport { env } from \"../../config/env\";\n\n/**\n * Read the configured gateway token. Returns undefined if not set,\n * which means auth is disabled (open access mode).\n */\nexport function getGatewayToken(): string | undefined {\n const token = env.GATEWAY_TOKEN;\n return token || undefined;\n}\n\n/**\n * Constant-time string comparison to prevent timing side-channel attacks.\n * Always compares the full length of `b` regardless of early mismatches.\n */\nexport function timingSafeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) {\n // Still iterate to maintain roughly constant time\n let result = 1;\n for (let i = 0; i < b.length; i++) {\n result |= b.charCodeAt(i) ^ (a.charCodeAt(i % (a.length || 1)) || 0);\n }\n return false;\n }\n let result = 0;\n for (let i = 0; i < a.length; i++) {\n result |= a.charCodeAt(i) ^ b.charCodeAt(i);\n }\n return result === 0;\n}\n","/**\n * Auth Middleware — Gateway Token (OpenClaw-style)\n *\n * When GATEWAY_TOKEN is not set: all routes pass through (open mode for self-hosted).\n * When GATEWAY_TOKEN is set: requires Bearer token matching the gateway token,\n * with fallback to session tokens and API keys for programmatic access.\n */\n\nimport type { Context, MiddlewareHandler, Next } from \"hono\";\nimport { validateApiKey, hasPermission, type Permission } from \"./api-key-manager\";\nimport { validateSession } from \"./session-manager\";\nimport { logAudit } from \"./audit-logger\";\nimport { getGatewayToken, timingSafeEqual } from \"./gateway-utils\";\n\n// Routes that never require auth\nconst PUBLIC_ROUTES = [\"/health\", \"/api/system/status\", \"/api/pair\", \"/api/sdk/register\"];\n\n// SDK routes have their own internal sdkAuth middleware\nconst SDK_PREFIX = \"/api/sdk/\";\n\nexport function authMiddleware(): MiddlewareHandler {\n return async (c: Context, next: Next) => {\n const path = c.req.path;\n\n // 1. Public routes — always pass through\n if (PUBLIC_ROUTES.includes(path)) {\n return next();\n }\n\n // 2. SDK routes (except register) — skip, SDK's own sdkAuth handles them\n if (path.startsWith(SDK_PREFIX) && path !== \"/api/sdk/register\") {\n return next();\n }\n\n // 3. If GATEWAY_TOKEN is not configured, auth is disabled (open mode)\n const gatewayToken = getGatewayToken();\n if (!gatewayToken) {\n c.set(\"userId\", \"local\");\n c.set(\"permissions\", [\"*\"]);\n c.set(\"authMethod\", \"none\");\n return next();\n }\n\n // 4. GATEWAY_TOKEN is set — require authentication\n const authHeader = c.req.header(\"Authorization\");\n\n if (!authHeader || !authHeader.startsWith(\"Bearer \")) {\n return c.json({ error: \"Missing or invalid authorization header\" }, 401);\n }\n\n const token = authHeader.slice(7);\n\n // 4a. Check gateway token (fast, constant-time comparison)\n if (timingSafeEqual(token, gatewayToken)) {\n c.set(\"userId\", \"gateway\");\n c.set(\"permissions\", [\"*\"]);\n c.set(\"authMethod\", \"gateway\");\n\n logAudit({\n userId: \"gateway\",\n action: \"login\",\n resource: \"session\",\n details: { method: \"gateway_token\", path },\n }).catch(() => {});\n\n return next();\n }\n\n // 4b. Fall back to session validation\n const sessionInfo = await validateSession(token);\n if (sessionInfo) {\n c.set(\"userId\", sessionInfo.userId);\n c.set(\"permissions\", [\"*\"]);\n c.set(\"authMethod\", \"session\");\n\n logAudit({\n userId: sessionInfo.userId,\n action: \"login\",\n resource: \"session\",\n details: { method: \"session\", path },\n }).catch(() => {});\n\n return next();\n }\n\n // 4c. Fall back to API key validation\n const apiKeyResult = await validateApiKey(token);\n if (apiKeyResult.valid && apiKeyResult.apiKey && apiKeyResult.userId) {\n c.set(\"userId\", apiKeyResult.userId);\n c.set(\"permissions\", apiKeyResult.apiKey.permissions);\n c.set(\"authMethod\", \"api_key\");\n\n logAudit({\n userId: apiKeyResult.userId,\n action: \"login\",\n resource: \"api_key\",\n details: { method: \"api_key\", keyId: apiKeyResult.apiKey.id, path },\n }).catch(() => {});\n\n return next();\n }\n\n return c.json({ error: \"Invalid or expired credentials\" }, 401);\n };\n}\n\nexport function requirePermission(\n ...requiredPermissions: Permission[]\n): MiddlewareHandler {\n return async (c: Context, next: Next) => {\n const permissions = c.get(\"permissions\") as Permission[] | undefined;\n\n if (!permissions) {\n return c.json({ error: \"Not authenticated\" }, 401);\n }\n\n // Full access wildcard\n if (permissions.includes(\"*\")) {\n return next();\n }\n\n for (const required of requiredPermissions) {\n if (!hasPermission(permissions, required)) {\n return c.json({ error: \"Insufficient permissions\" }, 403);\n }\n }\n\n return next();\n };\n}\n\nexport function getAuthUserId(c: Context): string | undefined {\n return c.get(\"userId\") as string | undefined;\n}\n","/**\n * OSINT API Routes\n *\n * Provides REST endpoints for the Graph Explorer frontend component.\n * Queries the Postgres-backed knowledge graph (graphEntities / graphRelationships)\n * and exposes enrichment, entity-resolution, and analytics helpers.\n */\n\nimport { Hono } from \"hono\";\nimport { db } from \"../../../db\";\nimport { graphEntities, graphRelationships } from \"../../../db/schema\";\nimport { eq, sql, desc, ilike, and } from \"drizzle-orm\";\nimport { env } from \"../../../config/env\";\nimport { createPublicRecords } from \"../../../integrations/public-records\";\nimport {\n resolveEntity,\n type EntityCandidate,\n} from \"../../../core/intelligence/entity-resolution\";\n\n// Lazy-init the public records facade (avoid startup cost if OSINT is disabled)\nlet _publicRecords: ReturnType<typeof createPublicRecords> | null = null;\nfunction getPublicRecords() {\n if (!_publicRecords) _publicRecords = createPublicRecords();\n return _publicRecords;\n}\n\nconst osint = new Hono();\n\n// ---------------------------------------------------------------------------\n// Middleware — gate every route behind OSINT_ENABLED\n// ---------------------------------------------------------------------------\n\nosint.use(\"*\", async (c, next) => {\n if (!env.OSINT_ENABLED) {\n return c.json({ error: \"OSINT features are disabled\" }, 403);\n }\n await next();\n});\n\n// ---------------------------------------------------------------------------\n// GET /graph — graph data for D3 force-directed visualisation\n// ---------------------------------------------------------------------------\n\nosint.get(\"/graph\", async (c) => {\n try {\n const userId = c.req.query(\"userId\") || \"system\";\n const limit = Math.min(parseInt(c.req.query(\"limit\") || \"200\", 10), 1000);\n\n // Fetch entities\n const entities = await db\n .select({\n id: graphEntities.id,\n name: graphEntities.name,\n type: graphEntities.type,\n importance: graphEntities.importance,\n description: graphEntities.description,\n attributes: graphEntities.attributes,\n aliases: graphEntities.aliases,\n })\n .from(graphEntities)\n .orderBy(desc(graphEntities.importance))\n .limit(limit);\n\n const entityIds = entities.map((e) => e.id);\n\n if (entityIds.length === 0) {\n return c.json({ nodes: [], edges: [] });\n }\n\n // Fetch relationships where both source and target are in the entity set\n const relationships = await db\n .select({\n id: graphRelationships.id,\n source: graphRelationships.sourceEntityId,\n target: graphRelationships.targetEntityId,\n type: graphRelationships.type,\n strength: graphRelationships.strength,\n })\n .from(graphRelationships)\n .where(\n and(\n sql`${graphRelationships.sourceEntityId} = ANY(${sql`ARRAY[${sql.join(entityIds.map(id => sql`${id}`), sql`, `)}]::uuid[]`})`,\n sql`${graphRelationships.targetEntityId} = ANY(${sql`ARRAY[${sql.join(entityIds.map(id => sql`${id}`), sql`, `)}]::uuid[]`})`\n )\n );\n\n return c.json({\n nodes: entities,\n edges: relationships,\n stats: {\n totalEntities: entities.length,\n totalRelationships: relationships.length,\n totalSources: new Set(\n entities.flatMap((e) => {\n const srcs = (e.attributes as any)?.sources;\n return Array.isArray(srcs) ? srcs.map((s: any) => s.type || \"unknown\") : [];\n })\n ).size,\n },\n });\n } catch (error) {\n console.error(\"[OSINT API] /graph error:\", error);\n return c.json({ error: \"Failed to fetch graph data\" }, 500);\n }\n});\n\n// ---------------------------------------------------------------------------\n// GET /entity/:id — single entity with relationships\n// ---------------------------------------------------------------------------\n\nosint.get(\"/entity/:id\", async (c) => {\n try {\n const id = c.req.param(\"id\");\n\n const results = await db\n .select()\n .from(graphEntities)\n .where(eq(graphEntities.id, id))\n .limit(1);\n\n if (results.length === 0) {\n return c.json({ error: \"Entity not found\" }, 404);\n }\n\n const entity = results[0];\n\n // Relationships where this entity is the source\n const outgoing = await db\n .select({\n id: graphRelationships.id,\n sourceEntityId: graphRelationships.sourceEntityId,\n targetEntityId: graphRelationships.targetEntityId,\n type: graphRelationships.type,\n strength: graphRelationships.strength,\n context: graphRelationships.context,\n attributes: graphRelationships.attributes,\n targetName: graphEntities.name,\n })\n .from(graphRelationships)\n .innerJoin(graphEntities, eq(graphRelationships.targetEntityId, graphEntities.id))\n .where(eq(graphRelationships.sourceEntityId, id));\n\n // Relationships where this entity is the target\n const incoming = await db\n .select({\n id: graphRelationships.id,\n sourceEntityId: graphRelationships.sourceEntityId,\n targetEntityId: graphRelationships.targetEntityId,\n type: graphRelationships.type,\n strength: graphRelationships.strength,\n context: graphRelationships.context,\n attributes: graphRelationships.attributes,\n sourceName: graphEntities.name,\n })\n .from(graphRelationships)\n .innerJoin(graphEntities, eq(graphRelationships.sourceEntityId, graphEntities.id))\n .where(eq(graphRelationships.targetEntityId, id));\n\n return c.json({\n entity,\n relationships: {\n outgoing,\n incoming,\n },\n });\n } catch (error) {\n console.error(\"[OSINT API] /entity/:id error:\", error);\n return c.json({ error: \"Failed to fetch entity\" }, 500);\n }\n});\n\n// ---------------------------------------------------------------------------\n// External API search helper\n// ---------------------------------------------------------------------------\n\n/**\n * Normalize FEC-style names from \"LAST, FIRST MIDDLE\" to \"First Middle Last\".\n */\nfunction normalizeFECName(raw: string): string {\n if (!raw.includes(\",\")) return toTitleCase(raw);\n const [last, ...rest] = raw.split(\",\").map((s) => s.trim());\n const first = rest.join(\" \").trim();\n if (!first) return toTitleCase(last);\n return toTitleCase(`${first} ${last}`);\n}\n\nfunction toTitleCase(s: string): string {\n return s\n .toLowerCase()\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\n/**\n * Query FEC and OpenCorporates for a search term, resolve results into\n * the local knowledge graph, and return the newly-created entity IDs.\n */\nasync function searchExternalAPIs(query: string): Promise<string[]> {\n const pr = getPublicRecords();\n const newEntityIds: string[] = [];\n\n // Run FEC (candidates + committees) and OpenCorporates in parallel.\n // Each call is wrapped so a single API failure doesn't kill the whole search.\n const [fecCandidates, fecCommittees, ocCompanies] = await Promise.all([\n pr.fec\n .searchCandidates(query)\n .then((r) => r.slice(0, 10))\n .catch((err) => {\n console.warn(\"[OSINT API] FEC candidates search failed:\", err.message);\n return [] as any[];\n }),\n pr.fec\n .searchCommittees(query)\n .then((r) => r.slice(0, 10))\n .catch((err) => {\n console.warn(\"[OSINT API] FEC committees search failed:\", err.message);\n return [] as any[];\n }),\n pr.opencorporates\n .searchCompanies(query, \"us\")\n .then((r) => r.slice(0, 10))\n .catch((err) => {\n console.warn(\"[OSINT API] OpenCorporates search failed:\", err.message);\n return [] as any[];\n }),\n ]);\n\n // Build entity candidates\n const candidates: EntityCandidate[] = [];\n\n for (const c of fecCandidates) {\n candidates.push({\n name: normalizeFECName(c.name),\n type: \"person\",\n source: \"fec\",\n identifiers: { fecId: c.candidateId },\n attributes: {\n party: c.party,\n office: c.office,\n state: c.state,\n district: c.district,\n cycles: c.cycles,\n },\n });\n }\n\n for (const c of fecCommittees) {\n candidates.push({\n name: toTitleCase(c.name),\n type: \"committee\",\n source: \"fec\",\n identifiers: { fecId: c.committeeId },\n attributes: {\n designation: c.designation,\n committeeType: c.type,\n party: c.party,\n state: c.state,\n treasurerName: c.treasurerName,\n },\n });\n }\n\n for (const c of ocCompanies) {\n candidates.push({\n name: c.name,\n type: \"organization\",\n source: \"opencorporates\",\n attributes: {\n companyNumber: c.companyNumber,\n jurisdiction: c.jurisdictionCode,\n status: c.status,\n companyType: c.companyType,\n incorporationDate: c.incorporationDate,\n registeredAddress: c.registeredAddress,\n openCorporatesUrl: c.openCorporatesUrl,\n },\n });\n }\n\n // Resolve each candidate (dedup / insert) — run sequentially to avoid\n // DB race conditions on the same entity name\n for (const candidate of candidates) {\n try {\n const resolved = await resolveEntity(candidate);\n newEntityIds.push(resolved.entityId);\n } catch (err: any) {\n console.warn(`[OSINT API] Entity resolution failed for \"${candidate.name}\":`, err.message);\n }\n }\n\n return [...new Set(newEntityIds)]; // deduplicate\n}\n\n// ---------------------------------------------------------------------------\n// GET /search — search entities by name\n// ---------------------------------------------------------------------------\n\nosint.get(\"/search\", async (c) => {\n try {\n const q = c.req.query(\"q\");\n const type = c.req.query(\"type\");\n const limit = Math.min(parseInt(c.req.query(\"limit\") || \"25\", 10), 100);\n\n if (!q) {\n return c.json({ error: \"Query parameter 'q' is required\" }, 400);\n }\n\n // 1. Local DB search\n let query = db\n .select()\n .from(graphEntities)\n .where(\n type\n ? and(ilike(graphEntities.name, `%${q}%`), eq(graphEntities.type, type as any))\n : ilike(graphEntities.name, `%${q}%`)\n )\n .orderBy(desc(graphEntities.importance))\n .limit(limit);\n\n let results = await query;\n let externalSearched = false;\n\n // 2. If local results are sparse, search external APIs and re-query\n if (results.length < 3 && q.length >= 2) {\n try {\n console.log(`[OSINT API] Local results (${results.length}) < 3 for \"${q}\", querying external APIs...`);\n const externalIds = await searchExternalAPIs(q);\n externalSearched = true;\n\n if (externalIds.length > 0) {\n // Re-run local search: match by name OR by the newly-ingested entity IDs\n // (needed because FEC names like \"PELOSI, NANCY\" get normalized to \"Nancy Pelosi\"\n // but the ILIKE may still not match all variations)\n const nameCondition = type\n ? and(ilike(graphEntities.name, `%${q}%`), eq(graphEntities.type, type as any))\n : ilike(graphEntities.name, `%${q}%`);\n\n const idCondition = sql`${graphEntities.id} = ANY(${sql`ARRAY[${sql.join(externalIds.map((id: string) => sql`${id}`), sql`, `)}]::uuid[]`})`;\n\n results = await db\n .select()\n .from(graphEntities)\n .where(sql`(${nameCondition}) OR (${idCondition})`)\n .orderBy(desc(graphEntities.importance))\n .limit(limit);\n }\n } catch (extErr: any) {\n console.warn(\"[OSINT API] External search failed (graceful):\", extErr.message);\n }\n }\n\n // 3. Fetch relationships between matched entities for graph view\n const entityIds = results.map((e: any) => e.id);\n let edges: any[] = [];\n if (entityIds.length > 1) {\n edges = await db\n .select({\n id: graphRelationships.id,\n source: graphRelationships.sourceEntityId,\n target: graphRelationships.targetEntityId,\n type: graphRelationships.type,\n strength: graphRelationships.strength,\n })\n .from(graphRelationships)\n .where(\n and(\n sql`${graphRelationships.sourceEntityId} = ANY(${sql`ARRAY[${sql.join(entityIds.map((id: string) => sql`${id}`), sql`, `)}]::uuid[]`})`,\n sql`${graphRelationships.targetEntityId} = ANY(${sql`ARRAY[${sql.join(entityIds.map((id: string) => sql`${id}`), sql`, `)}]::uuid[]`})`\n )\n );\n }\n\n return c.json({ results, edges, total: results.length, externalSearched });\n } catch (error) {\n console.error(\"[OSINT API] /search error:\", error);\n return c.json({ error: \"Search failed\" }, 500);\n }\n});\n\n// ---------------------------------------------------------------------------\n// POST /enrich — trigger enrichment for an entity\n// ---------------------------------------------------------------------------\n\nosint.post(\"/enrich\", async (c) => {\n try {\n const body = await c.req.json<{\n entityId: string;\n sources?: string[];\n depth?: number;\n }>();\n\n if (!body.entityId) {\n return c.json({ error: \"entityId is required\" }, 400);\n }\n\n // Verify entity exists\n const entity = await db\n .select()\n .from(graphEntities)\n .where(eq(graphEntities.id, body.entityId))\n .limit(1);\n\n if (entity.length === 0) {\n return c.json({ error: \"Entity not found\" }, 404);\n }\n\n // Dynamic import — the enrichment pipeline may not be built yet\n const { enrichEntity } = await import(\n \"../../../core/intelligence/enrichment-pipeline\"\n );\n\n const result = await enrichEntity(body.entityId, {\n sources: body.sources,\n depth: body.depth ?? 1,\n });\n\n return c.json({ success: true, result });\n } catch (error: any) {\n // Distinguish \"module not found\" from runtime errors\n if (error?.code === \"MODULE_NOT_FOUND\" || error?.message?.includes(\"Cannot find module\")) {\n return c.json({ error: \"Enrichment pipeline is not available\" }, 501);\n }\n console.error(\"[OSINT API] /enrich error:\", error);\n return c.json({ error: \"Enrichment failed\" }, 500);\n }\n});\n\n// ---------------------------------------------------------------------------\n// GET /financial-flow — Sankey diagram data\n// ---------------------------------------------------------------------------\n\nconst FINANCIAL_REL_TYPES = [\"donated_to\", \"funded\", \"awarded_contract\", \"grant_to\", \"paid\"];\n\nosint.get(\"/financial-flow\", async (c) => {\n try {\n const entityId = c.req.query(\"entityId\");\n\n if (!entityId) {\n return c.json({ error: \"Query parameter 'entityId' is required\" }, 400);\n }\n\n // Verify entity exists\n const rootEntity = await db\n .select({ id: graphEntities.id, name: graphEntities.name })\n .from(graphEntities)\n .where(eq(graphEntities.id, entityId))\n .limit(1);\n\n if (rootEntity.length === 0) {\n return c.json({ error: \"Entity not found\" }, 404);\n }\n\n // Fetch financial relationships involving this entity (outgoing and incoming)\n const outgoing = await db\n .select({\n id: graphRelationships.id,\n sourceEntityId: graphRelationships.sourceEntityId,\n targetEntityId: graphRelationships.targetEntityId,\n type: graphRelationships.type,\n strength: graphRelationships.strength,\n attributes: graphRelationships.attributes,\n })\n .from(graphRelationships)\n .where(\n and(\n eq(graphRelationships.sourceEntityId, entityId),\n sql`${graphRelationships.type} = ANY(${sql`ARRAY[${sql.join(FINANCIAL_REL_TYPES.map(t => sql`${t}`), sql`, `)}]::text[]`})`\n )\n );\n\n const incoming = await db\n .select({\n id: graphRelationships.id,\n sourceEntityId: graphRelationships.sourceEntityId,\n targetEntityId: graphRelationships.targetEntityId,\n type: graphRelationships.type,\n strength: graphRelationships.strength,\n attributes: graphRelationships.attributes,\n })\n .from(graphRelationships)\n .where(\n and(\n eq(graphRelationships.targetEntityId, entityId),\n sql`${graphRelationships.type} = ANY(${sql`ARRAY[${sql.join(FINANCIAL_REL_TYPES.map(t => sql`${t}`), sql`, `)}]::text[]`})`\n )\n );\n\n const allRels = [...outgoing, ...incoming];\n\n // Collect unique entity IDs referenced in the relationships\n const relatedIds = new Set<string>();\n relatedIds.add(entityId);\n for (const rel of allRels) {\n relatedIds.add(rel.sourceEntityId);\n relatedIds.add(rel.targetEntityId);\n }\n\n // Fetch details for all involved entities\n const relatedEntities = await db\n .select({\n id: graphEntities.id,\n name: graphEntities.name,\n type: graphEntities.type,\n })\n .from(graphEntities)\n .where(sql`${graphEntities.id} = ANY(${sql`ARRAY[${sql.join([...relatedIds].map(id => sql`${id}`), sql`, `)}]::uuid[]`})`);\n\n // Build nodes with id, name, type, and aggregated value\n const entityMap = new Map(relatedEntities.map((e) => [e.id, e]));\n\n // Calculate total value flowing through each node\n const nodeValues = new Map<string, number>();\n for (const rel of allRels) {\n const attrs = (rel.attributes as Record<string, unknown>) || {};\n const amount = (attrs.amount as number) || rel.strength || 1;\n nodeValues.set(rel.sourceEntityId, (nodeValues.get(rel.sourceEntityId) || 0) + amount);\n nodeValues.set(rel.targetEntityId, (nodeValues.get(rel.targetEntityId) || 0) + amount);\n }\n\n const nodes = relatedEntities.map((e) => ({\n id: e.id,\n name: e.name,\n type: e.type,\n value: nodeValues.get(e.id) || 0,\n }));\n\n // Build links with string IDs\n const links = allRels\n .filter((rel) => entityMap.has(rel.sourceEntityId) && entityMap.has(rel.targetEntityId))\n .map((rel) => {\n const attrs = (rel.attributes as Record<string, unknown>) || {};\n return {\n source: rel.sourceEntityId,\n target: rel.targetEntityId,\n value: (attrs.amount as number) || rel.strength || 1,\n description: `${rel.type.replace(/_/g, \" \")}${attrs.period ? ` (${attrs.period})` : \"\"}`,\n };\n });\n\n return c.json({ nodes, links });\n } catch (error) {\n console.error(\"[OSINT API] /financial-flow error:\", error);\n return c.json({ error: \"Failed to fetch financial flow data\" }, 500);\n }\n});\n\n// ---------------------------------------------------------------------------\n// GET /duplicates — find potential duplicate entities\n// ---------------------------------------------------------------------------\n\nosint.get(\"/duplicates\", async (c) => {\n try {\n const threshold = parseFloat(c.req.query(\"threshold\") || \"0.85\");\n\n const { findDuplicates } = await import(\n \"../../../core/intelligence/entity-resolution\"\n );\n\n const duplicates = await findDuplicates(threshold);\n\n return c.json({ duplicates, total: duplicates.length });\n } catch (error) {\n console.error(\"[OSINT API] /duplicates error:\", error);\n return c.json({ error: \"Failed to find duplicates\" }, 500);\n }\n});\n\n// ---------------------------------------------------------------------------\n// GET /stats — OSINT graph statistics\n// ---------------------------------------------------------------------------\n\nosint.get(\"/stats\", async (c) => {\n try {\n // Total entities\n const totalEntitiesResult = await db\n .select({ count: sql<number>`count(*)::int` })\n .from(graphEntities);\n const totalEntities = totalEntitiesResult[0]?.count ?? 0;\n\n // Total relationships\n const totalRelsResult = await db\n .select({ count: sql<number>`count(*)::int` })\n .from(graphRelationships);\n const totalRelationships = totalRelsResult[0]?.count ?? 0;\n\n // Entities by type\n const entitiesByType = await db\n .select({\n type: graphEntities.type,\n count: sql<number>`count(*)::int`,\n })\n .from(graphEntities)\n .groupBy(graphEntities.type)\n .orderBy(desc(sql`count(*)`));\n\n // Top entities by mention count\n const topEntities = await db\n .select({\n id: graphEntities.id,\n name: graphEntities.name,\n type: graphEntities.type,\n mentionCount: graphEntities.mentionCount,\n importance: graphEntities.importance,\n })\n .from(graphEntities)\n .orderBy(desc(graphEntities.mentionCount))\n .limit(20);\n\n return c.json({\n totalEntities,\n totalRelationships,\n entitiesByType,\n topEntities,\n });\n } catch (error) {\n console.error(\"[OSINT API] /stats error:\", error);\n return c.json({ error: \"Failed to fetch statistics\" }, 500);\n }\n});\n\nexport { osint as osintRoutes };\n","/**\n * Email API Routes\n *\n * Provides REST endpoints for reading, sending, searching, and managing emails\n * via IMAP and SMTP. Supports Dovecot master-user auth and direct credential auth.\n */\n\nimport { Hono } from \"hono\";\nimport { env } from \"../../../config/env\";\nimport {\n ImapClient,\n type EmailMessage,\n type EmailAttachment,\n} from \"../../../integrations/email/imap-client\";\nimport { SmtpClient } from \"../../../integrations/email/smtp-client\";\n\nconst email = new Hono();\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction createImapClient(emailAddress: string): ImapClient {\n if (env.EMAIL_MASTER_USER && env.EMAIL_MASTER_PASSWORD) {\n return new ImapClient({\n host: env.EMAIL_LOCAL_IMAP_HOST || \"127.0.0.1\",\n port: env.EMAIL_LOCAL_IMAP_PORT || 993,\n secure: true,\n user: `${emailAddress}*${env.EMAIL_MASTER_USER}`,\n password: env.EMAIL_MASTER_PASSWORD,\n tls: { rejectUnauthorized: false },\n });\n } else if (env.EMAIL_USER && env.EMAIL_PASSWORD) {\n return new ImapClient({\n host: env.EMAIL_IMAP_HOST || \"imap.gmail.com\",\n port: env.EMAIL_IMAP_PORT || 993,\n secure: env.EMAIL_IMAP_SECURE !== false,\n user: env.EMAIL_USER,\n password: env.EMAIL_PASSWORD,\n });\n }\n throw new Error(\"Email not configured\");\n}\n\nfunction createSmtpClient(fromAddress: string): SmtpClient {\n if (env.EMAIL_MASTER_USER) {\n return new SmtpClient(\n {\n host: env.EMAIL_LOCAL_SMTP_HOST || \"127.0.0.1\",\n port: env.EMAIL_LOCAL_SMTP_PORT || 25,\n secure: false,\n auth: { user: \"\", pass: \"\" },\n tls: { rejectUnauthorized: false },\n },\n fromAddress\n );\n } else if (env.EMAIL_USER && env.EMAIL_PASSWORD) {\n return new SmtpClient(\n {\n host: env.EMAIL_SMTP_HOST || \"smtp.gmail.com\",\n port: env.EMAIL_SMTP_PORT || 587,\n secure: env.EMAIL_SMTP_SECURE === true,\n auth: { user: env.EMAIL_USER, pass: env.EMAIL_PASSWORD },\n },\n fromAddress\n );\n }\n throw new Error(\"Email not configured\");\n}\n\nfunction serializeEmail(email: EmailMessage, includeBody: boolean): Record<string, unknown> {\n const serialized: Record<string, unknown> = {\n id: email.id,\n uid: email.uid,\n messageId: email.messageId,\n subject: email.subject,\n from: email.from,\n to: email.to,\n cc: email.cc,\n bcc: email.bcc,\n date: email.date.toISOString(),\n text: includeBody ? email.text : email.snippet,\n html: includeBody ? email.html : \"\",\n snippet: email.snippet,\n attachments: email.attachments.map((a) => ({\n filename: a.filename,\n contentType: a.contentType,\n size: a.size,\n contentId: a.contentId,\n hasContent: !!a.content,\n })),\n flags: email.flags,\n labels: email.labels,\n threadId: email.threadId,\n inReplyTo: email.inReplyTo,\n references: email.references,\n headers: Object.fromEntries(email.headers),\n };\n\n return serialized;\n}\n\n// ---------------------------------------------------------------------------\n// Middleware — gate every route behind email config check\n// ---------------------------------------------------------------------------\n\nemail.use(\"*\", async (c, next) => {\n if (!env.EMAIL_MASTER_USER && !env.EMAIL_USER) {\n return c.json(\n { error: \"Email not configured. Set EMAIL_MASTER_USER or EMAIL_USER in .env\" },\n 503\n );\n }\n await next();\n});\n\n// ---------------------------------------------------------------------------\n// GET /folders — list all mail folders\n// ---------------------------------------------------------------------------\n\nemail.get(\"/folders\", async (c) => {\n const emailAddress = c.req.query(\"email_address\");\n if (!emailAddress) {\n return c.json({ error: \"Query parameter 'email_address' is required\" }, 400);\n }\n\n let imap: ImapClient | null = null;\n try {\n imap = createImapClient(emailAddress);\n await imap.connect();\n const folders = await imap.listFolders();\n return c.json(folders);\n } catch (error) {\n console.error(\"[Email API] /folders error:\", error);\n return c.json(\n { error: error instanceof Error ? error.message : \"Failed to list folders\" },\n 500\n );\n } finally {\n if (imap) {\n await imap.disconnect().catch(() => {});\n }\n }\n});\n\n// ---------------------------------------------------------------------------\n// GET /inbox — list emails in a folder\n// ---------------------------------------------------------------------------\n\nemail.get(\"/inbox\", async (c) => {\n const emailAddress = c.req.query(\"email_address\");\n if (!emailAddress) {\n return c.json({ error: \"Query parameter 'email_address' is required\" }, 400);\n }\n\n const folder = c.req.query(\"folder\") || \"INBOX\";\n const limit = c.req.query(\"limit\") || \"20\";\n const offset = c.req.query(\"offset\") || \"0\";\n const unreadOnly = c.req.query(\"unread_only\") === \"true\";\n\n let imap: ImapClient | null = null;\n try {\n imap = createImapClient(emailAddress);\n await imap.connect();\n\n let emails: EmailMessage[];\n if (unreadOnly) {\n emails = await imap.searchEmails({\n folder,\n seen: false,\n limit: parseInt(limit),\n });\n } else {\n emails = await imap.fetchEmails(folder, {\n limit: parseInt(limit),\n offset: parseInt(offset),\n });\n }\n\n return c.json({\n emails: emails.map((e) => serializeEmail(e, false)),\n folder,\n });\n } catch (error) {\n console.error(\"[Email API] /inbox error:\", error);\n return c.json(\n { error: error instanceof Error ? error.message : \"Failed to fetch emails\" },\n 500\n );\n } finally {\n if (imap) {\n await imap.disconnect().catch(() => {});\n }\n }\n});\n\n// ---------------------------------------------------------------------------\n// GET /message/:uid — fetch a single email by UID\n// ---------------------------------------------------------------------------\n\nemail.get(\"/message/:uid\", async (c) => {\n const emailAddress = c.req.query(\"email_address\");\n if (!emailAddress) {\n return c.json({ error: \"Query parameter 'email_address' is required\" }, 400);\n }\n\n const uid = parseInt(c.req.param(\"uid\"));\n const folder = c.req.query(\"folder\") || \"INBOX\";\n\n let imap: ImapClient | null = null;\n try {\n imap = createImapClient(emailAddress);\n await imap.connect();\n\n const emailMsg = await imap.fetchEmail(uid, folder);\n if (!emailMsg) {\n return c.json({ error: \"Email not found\" }, 404);\n }\n\n await imap.markAsRead(uid, folder);\n\n return c.json(serializeEmail(emailMsg, true));\n } catch (error) {\n console.error(\"[Email API] /message/:uid error:\", error);\n return c.json(\n { error: error instanceof Error ? error.message : \"Failed to fetch email\" },\n 500\n );\n } finally {\n if (imap) {\n await imap.disconnect().catch(() => {});\n }\n }\n});\n\n// ---------------------------------------------------------------------------\n// GET /attachment/:uid/:index — download an attachment\n// ---------------------------------------------------------------------------\n\nemail.get(\"/attachment/:uid/:index\", async (c) => {\n const emailAddress = c.req.query(\"email_address\");\n if (!emailAddress) {\n return c.json({ error: \"Query parameter 'email_address' is required\" }, 400);\n }\n\n const uid = parseInt(c.req.param(\"uid\"));\n const index = parseInt(c.req.param(\"index\"));\n const folder = c.req.query(\"folder\") || \"INBOX\";\n\n let imap: ImapClient | null = null;\n try {\n imap = createImapClient(emailAddress);\n await imap.connect();\n\n const emailMsg = await imap.fetchEmail(uid, folder);\n if (!emailMsg) {\n return c.json({ error: \"Email not found\" }, 404);\n }\n\n const attachment = emailMsg.attachments[index];\n if (!attachment) {\n return c.json({ error: \"Attachment not found\" }, 404);\n }\n\n if (!attachment.content) {\n return c.json({ error: \"Attachment content not available\" }, 404);\n }\n\n return new Response(attachment.content, {\n headers: {\n \"Content-Type\": attachment.contentType || \"application/octet-stream\",\n \"Content-Disposition\": `attachment; filename=\"${attachment.filename}\"`,\n \"Content-Length\": String(attachment.size || attachment.content.length),\n },\n });\n } catch (error) {\n console.error(\"[Email API] /attachment/:uid/:index error:\", error);\n return c.json(\n { error: error instanceof Error ? error.message : \"Failed to fetch attachment\" },\n 500\n );\n } finally {\n if (imap) {\n await imap.disconnect().catch(() => {});\n }\n }\n});\n\n// ---------------------------------------------------------------------------\n// POST /send — send a new email\n// ---------------------------------------------------------------------------\n\nemail.post(\"/send\", async (c) => {\n try {\n const body = await c.req.json<{\n from: string;\n to: string | string[];\n cc?: string | string[];\n bcc?: string | string[];\n subject: string;\n text?: string;\n html?: string;\n attachments?: { filename: string; content: string; contentType: string }[];\n }>();\n\n if (!body.from || !body.to || !body.subject) {\n return c.json({ error: \"Fields 'from', 'to', and 'subject' are required\" }, 400);\n }\n\n const smtp = createSmtpClient(body.from);\n\n const convertedAttachments = body.attachments?.map((a) => ({\n filename: a.filename,\n contentType: a.contentType,\n size: Buffer.from(a.content, \"base64\").length,\n content: Buffer.from(a.content, \"base64\"),\n }));\n\n const result = await smtp.send({\n to: body.to,\n cc: body.cc,\n bcc: body.bcc,\n subject: body.subject,\n text: body.text,\n html: body.html,\n attachments: convertedAttachments,\n });\n\n return c.json(result);\n } catch (error) {\n console.error(\"[Email API] /send error:\", error);\n return c.json(\n { error: error instanceof Error ? error.message : \"Failed to send email\" },\n 500\n );\n }\n});\n\n// ---------------------------------------------------------------------------\n// POST /reply — reply to an existing email\n// ---------------------------------------------------------------------------\n\nemail.post(\"/reply\", async (c) => {\n const body = await c.req.json<{\n email_address: string;\n email_uid: number;\n folder?: string;\n body: string;\n html?: string;\n reply_all?: boolean;\n attachments?: { filename: string; content: string; contentType: string }[];\n }>();\n\n if (!body.email_address || !body.email_uid || !body.body) {\n return c.json(\n { error: \"Fields 'email_address', 'email_uid', and 'body' are required\" },\n 400\n );\n }\n\n const folder = body.folder || \"INBOX\";\n\n let imap: ImapClient | null = null;\n try {\n imap = createImapClient(body.email_address);\n await imap.connect();\n\n const originalEmail = await imap.fetchEmail(body.email_uid, folder);\n if (!originalEmail) {\n return c.json({ error: \"Original email not found\" }, 404);\n }\n\n const smtp = createSmtpClient(body.email_address);\n\n const convertedAttachments = body.attachments?.map((a) => ({\n filename: a.filename,\n contentType: a.contentType,\n size: Buffer.from(a.content, \"base64\").length,\n content: Buffer.from(a.content, \"base64\"),\n }));\n\n let result;\n if (body.reply_all) {\n result = await smtp.replyAll(\n originalEmail,\n { text: body.body, html: body.html, attachments: convertedAttachments },\n body.email_address\n );\n } else {\n result = await smtp.reply(originalEmail, {\n text: body.body,\n html: body.html,\n attachments: convertedAttachments,\n });\n }\n\n return c.json(result);\n } catch (error) {\n console.error(\"[Email API] /reply error:\", error);\n return c.json(\n { error: error instanceof Error ? error.message : \"Failed to send reply\" },\n 500\n );\n } finally {\n if (imap) {\n await imap.disconnect().catch(() => {});\n }\n }\n});\n\n// ---------------------------------------------------------------------------\n// POST /search — search emails\n// ---------------------------------------------------------------------------\n\nemail.post(\"/search\", async (c) => {\n const body = await c.req.json<{\n email_address: string;\n from?: string;\n to?: string;\n subject?: string;\n body?: string;\n since?: string;\n before?: string;\n unread_only?: boolean;\n folder?: string;\n limit?: number;\n }>();\n\n if (!body.email_address) {\n return c.json({ error: \"Field 'email_address' is required\" }, 400);\n }\n\n let imap: ImapClient | null = null;\n try {\n imap = createImapClient(body.email_address);\n await imap.connect();\n\n const searchOptions = {\n folder: body.folder || \"INBOX\",\n from: body.from,\n to: body.to,\n subject: body.subject,\n body: body.body,\n since: body.since ? new Date(body.since) : undefined,\n before: body.before ? new Date(body.before) : undefined,\n seen: body.unread_only ? false : undefined,\n limit: body.limit,\n };\n\n const emails = await imap.searchEmails(searchOptions);\n\n return c.json({\n emails: emails.map((e) => serializeEmail(e, false)),\n });\n } catch (error) {\n console.error(\"[Email API] /search error:\", error);\n return c.json(\n { error: error instanceof Error ? error.message : \"Search failed\" },\n 500\n );\n } finally {\n if (imap) {\n await imap.disconnect().catch(() => {});\n }\n }\n});\n\n// ---------------------------------------------------------------------------\n// POST /flag — flag/unflag/read/unread/delete an email\n// ---------------------------------------------------------------------------\n\nemail.post(\"/flag\", async (c) => {\n const body = await c.req.json<{\n email_address: string;\n uid: number;\n folder?: string;\n action: \"read\" | \"unread\" | \"flag\" | \"unflag\" | \"delete\";\n }>();\n\n if (!body.email_address || !body.uid || !body.action) {\n return c.json(\n { error: \"Fields 'email_address', 'uid', and 'action' are required\" },\n 400\n );\n }\n\n const folder = body.folder || \"INBOX\";\n\n let imap: ImapClient | null = null;\n try {\n imap = createImapClient(body.email_address);\n await imap.connect();\n\n switch (body.action) {\n case \"read\":\n await imap.markAsRead(body.uid, folder);\n break;\n case \"unread\":\n await imap.markAsUnread(body.uid, folder);\n break;\n case \"flag\":\n await imap.flagEmail(body.uid, folder);\n break;\n case \"unflag\":\n await imap.unflagEmail(body.uid, folder);\n break;\n case \"delete\":\n await imap.deleteEmail(body.uid, folder);\n break;\n default:\n return c.json({ error: `Unknown action: ${body.action}` }, 400);\n }\n\n return c.json({ success: true });\n } catch (error) {\n console.error(\"[Email API] /flag error:\", error);\n return c.json(\n { error: error instanceof Error ? error.message : \"Failed to perform action\" },\n 500\n );\n } finally {\n if (imap) {\n await imap.disconnect().catch(() => {});\n }\n }\n});\n\nexport { email as emailRoutes };\n","import { Hono } from \"hono\";\nimport { executeTool, TOOLS } from \"../../../tools\";\nimport { chatWithTools, type Message, type ChatOptions } from \"../../../core/brain\";\nimport { prioritizeTools, getAppProfile } from \"../../../core/app-profiles\";\nimport { storeMemory, searchMemories } from \"../../../core/memory\";\n\n// In-memory app registry (production would use DB)\ninterface RegisteredApp {\n id: string;\n name: string;\n type: string;\n apiKey: string;\n callbackUrl?: string;\n registeredAt: Date;\n lastSeen: Date;\n}\n\nconst registeredApps = new Map<string, RegisteredApp>();\n\n// Generate a simple UUID-like key\nfunction generateApiKey(): string {\n const chars = \"abcdefghijklmnopqrstuvwxyz0123456789\";\n const segments = [8, 4, 4, 4, 12];\n return \"osk_\" + segments\n .map((len) => Array.from({ length: len }, () => chars[Math.floor(Math.random() * chars.length)]).join(\"\"))\n .join(\"-\");\n}\n\n// Auth middleware - validates API key\nasync function sdkAuth(c: any, next: any) {\n const authHeader = c.req.header(\"Authorization\");\n if (!authHeader?.startsWith(\"Bearer osk_\")) {\n return c.json({ error: \"Invalid or missing API key. Register at POST /api/sdk/register\" }, 401);\n }\n\n const apiKey = authHeader.slice(7);\n const app = Array.from(registeredApps.values()).find((a) => a.apiKey === apiKey);\n\n if (!app) {\n return c.json({ error: \"Unknown API key. Register at POST /api/sdk/register\" }, 401);\n }\n\n app.lastSeen = new Date();\n c.set(\"sdkApp\", app);\n await next();\n}\n\nconst sdkRoutes = new Hono();\n\n// Registration endpoint (no auth required)\nsdkRoutes.post(\"/register\", async (c) => {\n try {\n const body = await c.req.json<{\n name: string;\n type: string;\n callbackUrl?: string;\n }>();\n\n if (!body.name || !body.type) {\n return c.json({ error: \"name and type are required\" }, 400);\n }\n\n // Check if app already registered\n const existing = Array.from(registeredApps.values()).find(\n (a) => a.name === body.name && a.type === body.type\n );\n if (existing) {\n return c.json({\n id: existing.id,\n apiKey: existing.apiKey,\n message: \"App already registered\",\n });\n }\n\n const id = crypto.randomUUID();\n const apiKey = generateApiKey();\n const app: RegisteredApp = {\n id,\n name: body.name,\n type: body.type,\n apiKey,\n callbackUrl: body.callbackUrl,\n registeredAt: new Date(),\n lastSeen: new Date(),\n };\n\n registeredApps.set(id, app);\n\n return c.json({\n id,\n apiKey,\n message: \"App registered successfully. Use this API key in Authorization: Bearer header.\",\n endpoints: {\n chat: \"POST /api/sdk/chat\",\n notify: \"POST /api/sdk/notify\",\n memory_store: \"POST /api/sdk/memory\",\n memory_search: \"POST /api/sdk/memory/search\",\n tools_list: \"GET /api/sdk/tools\",\n tools_execute: \"POST /api/sdk/tools/execute\",\n agent_spawn: \"POST /api/sdk/agent/spawn\",\n status: \"GET /api/sdk/status\",\n },\n });\n } catch (error) {\n return c.json({ error: \"Registration failed\" }, 500);\n }\n});\n\n// All remaining routes require auth\nsdkRoutes.use(\"/*\", sdkAuth);\n\n// Chat - AI-powered conversation with OpenSentinel\nsdkRoutes.post(\"/chat\", async (c) => {\n try {\n const app = c.get(\"sdkApp\") as RegisteredApp;\n const body = await c.req.json<{\n message: string;\n context?: string;\n useTools?: boolean;\n systemPrompt?: string;\n }>();\n\n if (!body.message) {\n return c.json({ error: \"message is required\" }, 400);\n }\n\n const messages: Message[] = [];\n if (body.context) {\n messages.push({\n role: \"user\",\n content: `[Context from ${app.name} (${app.type})]: ${body.context}`,\n });\n messages.push({\n role: \"assistant\",\n content: \"Understood, I have the context. How can I help?\",\n });\n }\n messages.push({ role: \"user\", content: body.message });\n\n const toolsUsed: string[] = [];\n const response = body.useTools !== false\n ? await chatWithTools(messages, `sdk:${app.id}`, (tool) => toolsUsed.push(tool), { appType: app.type })\n : await (await import(\"../../../core/brain\")).chat(messages, body.systemPrompt);\n\n // Store interaction in memory for cross-app intelligence\n await storeMemory({\n content: `[${app.name}] User asked: ${body.message.slice(0, 200)}`,\n type: \"episodic\",\n importance: 5,\n userId: `sdk:${app.id}`,\n source: `sdk:${app.name}`,\n provenance: `sdk:${app.type}`,\n }).catch(() => {}); // Non-blocking\n\n return c.json({\n content: response.content,\n toolsUsed,\n usage: {\n inputTokens: response.inputTokens,\n outputTokens: response.outputTokens,\n },\n app: app.name,\n });\n } catch (error) {\n console.error(\"SDK chat error:\", error);\n return c.json({ error: \"Chat failed\" }, 500);\n }\n});\n\n// Notify - Send notification through OpenSentinel channels\nsdkRoutes.post(\"/notify\", async (c) => {\n try {\n const app = c.get(\"sdkApp\") as RegisteredApp;\n const body = await c.req.json<{\n channel: \"telegram\" | \"discord\" | \"slack\" | \"email\" | \"all\";\n message: string;\n recipient?: string;\n priority?: \"low\" | \"normal\" | \"high\" | \"urgent\";\n }>();\n\n if (!body.channel || !body.message) {\n return c.json({ error: \"channel and message are required\" }, 400);\n }\n\n const sent: string[] = [];\n const prefix = `[${app.name}] `;\n const fullMessage = prefix + body.message;\n\n const channels = body.channel === \"all\"\n ? [\"telegram\", \"discord\", \"slack\"]\n : [body.channel];\n\n for (const ch of channels) {\n try {\n switch (ch) {\n case \"telegram\": {\n const { sendTelegramMessage } = await import(\"../../telegram\");\n await sendTelegramMessage(fullMessage);\n sent.push(\"telegram\");\n break;\n }\n case \"discord\": {\n const { sendDiscordMessage } = await import(\"../../discord\");\n await sendDiscordMessage(fullMessage);\n sent.push(\"discord\");\n break;\n }\n case \"slack\": {\n const { sendSlackMessage } = await import(\"../../slack\");\n await sendSlackMessage(fullMessage);\n sent.push(\"slack\");\n break;\n }\n case \"email\": {\n const { sendEmail } = await import(\"../../../integrations/email\");\n await sendEmail({\n to: body.recipient || \"\",\n subject: `${app.name} Notification`,\n text: body.message,\n });\n sent.push(\"email\");\n break;\n }\n }\n } catch (err) {\n // Channel not configured, skip\n }\n }\n\n return c.json({ sent, message: `Notification sent via: ${sent.join(\", \") || \"none (no channels configured)\"}` });\n } catch (error) {\n return c.json({ error: \"Notification failed\" }, 500);\n }\n});\n\n// Memory Store\nsdkRoutes.post(\"/memory\", async (c) => {\n try {\n const app = c.get(\"sdkApp\") as RegisteredApp;\n const body = await c.req.json<{\n content: string;\n type?: \"episodic\" | \"semantic\" | \"procedural\";\n importance?: number;\n metadata?: Record<string, any>;\n }>();\n\n if (!body.content) {\n return c.json({ error: \"content is required\" }, 400);\n }\n\n const memory = await storeMemory({\n content: body.content,\n type: body.type || \"semantic\",\n importance: body.importance || 5,\n userId: `sdk:${app.id}`,\n source: `sdk:${app.name}`,\n provenance: `sdk:${app.type}`,\n });\n\n return c.json(memory);\n } catch (error) {\n return c.json({ error: \"Memory store failed\" }, 500);\n }\n});\n\n// Memory Search\nsdkRoutes.post(\"/memory/search\", async (c) => {\n try {\n const app = c.get(\"sdkApp\") as RegisteredApp;\n const body = await c.req.json<{\n query: string;\n limit?: number;\n crossApp?: boolean;\n }>();\n\n if (!body.query) {\n return c.json({ error: \"query is required\" }, 400);\n }\n\n // Cross-app search uses no userId filter; app-specific uses sdk:appId\n const userId = body.crossApp ? undefined : `sdk:${app.id}`;\n const results = await searchMemories(body.query, userId, body.limit || 5);\n\n return c.json(results);\n } catch (error) {\n return c.json({ error: \"Memory search failed\" }, 500);\n }\n});\n\n// List available tools (prioritized for app type)\nsdkRoutes.get(\"/tools\", (c) => {\n const app = c.get(\"sdkApp\") as RegisteredApp;\n const ordered = prioritizeTools(TOOLS as any[], app.type);\n const profile = getAppProfile(app.type);\n const prioritySet = new Set(profile.priorityTools);\n\n const toolList = ordered.map((t: any) => ({\n name: t.name,\n description: t.description,\n priority: prioritySet.has(t.name),\n }));\n return c.json({ tools: toolList, count: toolList.length, appType: app.type });\n});\n\n// Execute a specific tool\nsdkRoutes.post(\"/tools/execute\", async (c) => {\n try {\n const body = await c.req.json<{\n tool: string;\n input: Record<string, any>;\n }>();\n\n if (!body.tool || !body.input) {\n return c.json({ error: \"tool and input are required\" }, 400);\n }\n\n const result = await executeTool(body.tool, body.input);\n return c.json({ tool: body.tool, result });\n } catch (error) {\n return c.json({ error: \"Tool execution failed\" }, 500);\n }\n});\n\n// Spawn a sub-agent\nsdkRoutes.post(\"/agent/spawn\", async (c) => {\n try {\n const app = c.get(\"sdkApp\") as RegisteredApp;\n const body = await c.req.json<{\n type: \"research\" | \"coding\" | \"writing\" | \"analysis\";\n task: string;\n context?: string;\n }>();\n\n if (!body.type || !body.task) {\n return c.json({ error: \"type and task are required\" }, 400);\n }\n\n // Use chat with tools to delegate to an agent\n const agentPrompt = `As a ${body.type} agent for ${app.name}, complete this task: ${body.task}${body.context ? `\\n\\nContext: ${body.context}` : \"\"}`;\n\n const messages: Message[] = [{ role: \"user\", content: agentPrompt }];\n const toolsUsed: string[] = [];\n const response = await chatWithTools(messages, `sdk:${app.id}:agent:${body.type}`, (tool) => toolsUsed.push(tool), { appType: app.type });\n\n return c.json({\n agent: body.type,\n result: response.content,\n toolsUsed,\n usage: {\n inputTokens: response.inputTokens,\n outputTokens: response.outputTokens,\n },\n });\n } catch (error) {\n return c.json({ error: \"Agent spawn failed\" }, 500);\n }\n});\n\n// Status\nsdkRoutes.get(\"/status\", (c) => {\n const app = c.get(\"sdkApp\") as RegisteredApp;\n const allApps = Array.from(registeredApps.values()).map((a) => ({\n id: a.id,\n name: a.name,\n type: a.type,\n registeredAt: a.registeredAt,\n lastSeen: a.lastSeen,\n }));\n\n return c.json({\n opensentinel: {\n status: \"online\",\n version: \"2.2.1\",\n uptime: process.uptime(),\n },\n currentApp: {\n id: app.id,\n name: app.name,\n type: app.type,\n },\n registeredApps: allApps,\n tools: (TOOLS as any[]).length,\n });\n});\n\nexport { sdkRoutes };\n","import { Hono } from \"hono\";\r\nimport {\r\n queryAuditLogs,\r\n getAuditChainIntegrity,\r\n type AuditQueryOptions,\r\n type AuditAction,\r\n type AuditResource,\r\n} from \"../../../core/security/audit-logger\";\r\nimport { checkRateLimit } from \"../../../core/security/rate-limiter\";\r\n\r\nconst adminRouter = new Hono();\r\n\r\n// Middleware for admin API key authentication\r\nasync function requireAdminKey(c: any, next: () => Promise<void>) {\r\n const apiKey = c.req.header(\"x-api-key\");\r\n if (!apiKey) {\r\n return c.json({ error: \"API key required\" }, 401);\r\n }\r\n\r\n const rateLimitResult = await checkRateLimit(apiKey, \"api/admin\");\r\n if (!rateLimitResult.allowed) {\r\n return c.json(\r\n { error: \"Rate limit exceeded\", retryAfter: rateLimitResult.retryAfterMs },\r\n 429\r\n );\r\n }\r\n\r\n await next();\r\n}\r\n\r\nadminRouter.use(\"*\", requireAdminKey);\r\n\r\n// GET /api/admin/audit-logs — Query audit logs with filters\r\nadminRouter.get(\"/audit-logs\", async (c) => {\r\n try {\r\n const url = new URL(c.req.url);\r\n const options: AuditQueryOptions = {};\r\n\r\n const userId = url.searchParams.get(\"userId\");\r\n if (userId) options.userId = userId;\r\n\r\n const action = url.searchParams.get(\"action\");\r\n if (action) options.action = action as AuditAction;\r\n\r\n const resource = url.searchParams.get(\"resource\");\r\n if (resource) options.resource = resource as AuditResource;\r\n\r\n const startDate = url.searchParams.get(\"startDate\");\r\n if (startDate) options.startDate = new Date(startDate);\r\n\r\n const endDate = url.searchParams.get(\"endDate\");\r\n if (endDate) options.endDate = new Date(endDate);\r\n\r\n const limit = url.searchParams.get(\"limit\");\r\n options.limit = limit ? Math.min(parseInt(limit, 10), 500) : 100;\r\n\r\n const offset = url.searchParams.get(\"offset\");\r\n options.offset = offset ? parseInt(offset, 10) : 0;\r\n\r\n const logs = await queryAuditLogs(options);\r\n\r\n return c.json({\r\n success: true,\r\n logs,\r\n count: logs.length,\r\n limit: options.limit,\r\n offset: options.offset,\r\n });\r\n } catch (err: any) {\r\n return c.json({ error: err.message || \"Failed to query audit logs\" }, 500);\r\n }\r\n});\r\n\r\n// GET /api/admin/audit-logs/integrity — Check audit chain integrity\r\nadminRouter.get(\"/audit-logs/integrity\", async (c) => {\r\n try {\r\n const integrity = await getAuditChainIntegrity();\r\n return c.json({ success: true, ...integrity });\r\n } catch (err: any) {\r\n return c.json({ error: err.message || \"Failed to check integrity\" }, 500);\r\n }\r\n});\r\n\r\n// GET /api/admin/incidents — Get recent security incidents (failed actions, errors)\r\nadminRouter.get(\"/incidents\", async (c) => {\r\n try {\r\n const url = new URL(c.req.url);\r\n const limit = Math.min(parseInt(url.searchParams.get(\"limit\") || \"50\", 10), 200);\r\n\r\n // Get failed/error audit entries as incidents\r\n const incidents = await queryAuditLogs({\r\n action: \"error\",\r\n limit,\r\n });\r\n\r\n return c.json({\r\n success: true,\r\n incidents,\r\n count: incidents.length,\r\n });\r\n } catch (err: any) {\r\n return c.json({ error: err.message || \"Failed to query incidents\" }, 500);\r\n }\r\n});\r\n\r\nexport default adminRouter;\r\n","import Redis from \"ioredis\";\nimport { env } from \"../../config/env\";\n\n// Redis connection for rate limiting\nconst redis = new Redis(env.REDIS_URL, {\n maxRetriesPerRequest: 3,\n retryDelayOnFailover: 100,\n});\n\nexport interface RateLimitConfig {\n windowMs: number; // Time window in milliseconds\n maxRequests: number; // Max requests per window\n}\n\nexport interface RateLimitResult {\n allowed: boolean;\n remaining: number;\n resetAt: Date;\n retryAfterMs?: number;\n}\n\n// Default rate limits by endpoint\nconst DEFAULT_LIMITS: Record<string, RateLimitConfig> = {\n \"api/chat\": { windowMs: 60000, maxRequests: 30 },\n \"api/chat/tools\": { windowMs: 60000, maxRequests: 20 },\n \"api/ask\": { windowMs: 60000, maxRequests: 30 },\n \"tool/shell\": { windowMs: 60000, maxRequests: 10 },\n \"tool/browser\": { windowMs: 60000, maxRequests: 10 },\n \"tool/file_write\": { windowMs: 60000, maxRequests: 20 },\n \"agent/spawn\": { windowMs: 3600000, maxRequests: 5 }, // 5 agents per hour\n default: { windowMs: 60000, maxRequests: 60 },\n};\n\nfunction getKey(identifier: string, endpoint: string): string {\n return `ratelimit:${identifier}:${endpoint}`;\n}\n\nexport async function checkRateLimit(\n identifier: string, // userId, IP, or API key\n endpoint: string,\n customConfig?: RateLimitConfig\n): Promise<RateLimitResult> {\n const config = customConfig || DEFAULT_LIMITS[endpoint] || DEFAULT_LIMITS.default;\n const { windowMs, maxRequests } = config;\n\n const key = getKey(identifier, endpoint);\n const now = Date.now();\n\n try {\n // Use Redis MULTI for atomic operations\n const multi = redis.multi();\n\n // Remove expired entries\n multi.zremrangebyscore(key, 0, now - windowMs);\n\n // Count current requests in window\n multi.zcard(key);\n\n // Add current request\n multi.zadd(key, now.toString(), `${now}:${Math.random()}`);\n\n // Set expiry on the key\n multi.pexpire(key, windowMs);\n\n const results = await multi.exec();\n\n if (!results) {\n // Redis error, allow request but log\n console.warn(\"[RateLimiter] Redis transaction failed, allowing request\");\n return {\n allowed: true,\n remaining: maxRequests,\n resetAt: new Date(now + windowMs),\n };\n }\n\n const currentCount = (results[1]?.[1] as number) || 0;\n const resetAt = new Date(now + windowMs);\n\n if (currentCount >= maxRequests) {\n // Remove the request we just added since we're rejecting\n await redis.zrem(key, `${now}:${Math.random()}`);\n\n return {\n allowed: false,\n remaining: 0,\n resetAt,\n retryAfterMs: windowMs - (now - (await getWindowStart(key, now, windowMs))),\n };\n }\n\n return {\n allowed: true,\n remaining: maxRequests - currentCount - 1,\n resetAt,\n };\n } catch (error) {\n // On Redis error, fail open (allow request)\n console.error(\"[RateLimiter] Error checking rate limit:\", error);\n return {\n allowed: true,\n remaining: maxRequests,\n resetAt: new Date(now + windowMs),\n };\n }\n}\n\nasync function getWindowStart(\n key: string,\n now: number,\n windowMs: number\n): Promise<number> {\n const oldest = await redis.zrange(key, 0, 0, \"WITHSCORES\");\n if (oldest.length >= 2) {\n return parseInt(oldest[1], 10);\n }\n return now - windowMs;\n}\n\nexport async function getRateLimitStatus(\n identifier: string,\n endpoint: string\n): Promise<{ count: number; remaining: number; resetAt: Date }> {\n const config = DEFAULT_LIMITS[endpoint] || DEFAULT_LIMITS.default;\n const { windowMs, maxRequests } = config;\n\n const key = getKey(identifier, endpoint);\n const now = Date.now();\n\n // Clean old entries\n await redis.zremrangebyscore(key, 0, now - windowMs);\n\n // Get current count\n const count = await redis.zcard(key);\n\n return {\n count,\n remaining: Math.max(0, maxRequests - count),\n resetAt: new Date(now + windowMs),\n };\n}\n\nexport async function resetRateLimit(\n identifier: string,\n endpoint: string\n): Promise<void> {\n const key = getKey(identifier, endpoint);\n await redis.del(key);\n}\n\nexport async function resetAllRateLimits(identifier: string): Promise<void> {\n const pattern = `ratelimit:${identifier}:*`;\n const keys = await redis.keys(pattern);\n if (keys.length > 0) {\n await redis.del(...keys);\n }\n}\n\n// Get all rate limit configs\nexport function getRateLimitConfigs(): Record<string, RateLimitConfig> {\n return { ...DEFAULT_LIMITS };\n}\n\n// Update rate limit config (in-memory only, resets on restart)\nexport function setRateLimitConfig(\n endpoint: string,\n config: RateLimitConfig\n): void {\n DEFAULT_LIMITS[endpoint] = config;\n}\n\n// Middleware helper for Hono\nexport function createRateLimitMiddleware(endpoint: string) {\n return async (c: { req: { header: (name: string) => string | undefined }; json: (data: unknown, status: number) => Response }, next: () => Promise<void>) => {\n // Try to get identifier from various sources\n const apiKey = c.req.header(\"x-api-key\");\n const ip = c.req.header(\"x-forwarded-for\") || c.req.header(\"x-real-ip\") || \"unknown\";\n\n const identifier = apiKey || ip;\n const result = await checkRateLimit(identifier, endpoint);\n\n if (!result.allowed) {\n return c.json(\n {\n error: \"Rate limit exceeded\",\n retryAfter: result.retryAfterMs,\n resetAt: result.resetAt.toISOString(),\n },\n 429\n );\n }\n\n await next();\n };\n}\n\n// Cleanup on shutdown\nexport async function closeRateLimiter(): Promise<void> {\n await redis.quit();\n}\n\nexport { redis as rateLimitRedis };\n"],"mappings":"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","names":["Hono","desc","eq","eq","and","randomBytes","createHash","createHash","and","eq","result","eq","and","and","eq","Hono","Hono","email","Hono","app","Hono","messages","Hono","Hono","Hono","messages","desc","eq","getAuditChainIntegrity"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/security/audit-logger.ts"],"sourcesContent":["import { createHmac, randomUUID } from \"crypto\";\nimport { db } from \"../../db\";\nimport { auditLogs, NewAuditLog } from \"../../db/schema\";\nimport { eq, and, gte, lte, desc, count, min, max, asc } from \"drizzle-orm\";\nimport { env } from \"../../config/env\";\n\nexport type AuditAction =\n | \"login\"\n | \"logout\"\n | \"session_create\"\n | \"session_invalidate\"\n | \"api_key_create\"\n | \"api_key_revoke\"\n | \"tool_use\"\n | \"chat_message\"\n | \"memory_create\"\n | \"memory_delete\"\n | \"memory_archive\"\n | \"settings_change\"\n | \"mode_change\"\n | \"agent_spawn\"\n | \"agent_complete\"\n | \"file_read\"\n | \"file_write\"\n | \"shell_execute\"\n | \"web_browse\"\n | \"error\";\n\nexport type AuditResource =\n | \"session\"\n | \"api_key\"\n | \"tool\"\n | \"chat\"\n | \"memory\"\n | \"settings\"\n | \"mode\"\n | \"agent\"\n | \"file\"\n | \"shell\"\n | \"browser\";\n\nexport interface AuditLogEntry {\n userId?: string;\n sessionId?: string;\n action: AuditAction;\n resource?: AuditResource;\n resourceId?: string;\n details?: Record<string, unknown>;\n ipAddress?: string;\n userAgent?: string;\n success?: boolean;\n}\n\n// --- Tamper-proof chain hashing helpers (SOC 2 compliance) ---\n\nlet _cachedSigningKey: string | null = null;\n\nfunction getAuditSigningKey(): string {\n if (_cachedSigningKey) return _cachedSigningKey;\n if (env.AUDIT_SIGNING_KEY) {\n _cachedSigningKey = env.AUDIT_SIGNING_KEY;\n return _cachedSigningKey;\n }\n _cachedSigningKey = randomUUID();\n console.warn(\n \"[audit-logger] AUDIT_SIGNING_KEY not set — using a random ephemeral key. \" +\n \"Set AUDIT_SIGNING_KEY in .env for persistent tamper-proof audit chains.\"\n );\n return _cachedSigningKey;\n}\n\nfunction signAuditEntry(\n sequenceNumber: number,\n action: string,\n userId: string | undefined,\n resource: string | undefined,\n detailsJson: string,\n timestamp: string,\n previousHash: string | null\n): string {\n const key = getAuditSigningKey();\n const data = [\n String(sequenceNumber),\n action,\n userId ?? \"\",\n resource ?? \"\",\n detailsJson,\n timestamp,\n previousHash ?? \"\",\n ].join(\"|\");\n return createHmac(\"sha256\", key).update(data).digest(\"hex\");\n}\n\nasync function getLastAuditEntry(): Promise<{\n sequenceNumber: number;\n entryHash: string;\n} | null> {\n const [last] = await db\n .select({\n sequenceNumber: auditLogs.sequenceNumber,\n entryHash: auditLogs.entryHash,\n })\n .from(auditLogs)\n .orderBy(desc(auditLogs.sequenceNumber))\n .limit(1);\n\n if (!last || last.sequenceNumber == null || last.entryHash == null) {\n return null;\n }\n\n return {\n sequenceNumber: last.sequenceNumber,\n entryHash: last.entryHash,\n };\n}\n\nexport async function logAudit(entry: AuditLogEntry): Promise<string> {\n // Fetch previous chain entry for tamper-proof linking\n const last = await getLastAuditEntry();\n const sequenceNumber = (last?.sequenceNumber ?? 0) + 1;\n const previousHash = last?.entryHash ?? null;\n\n const timestamp = new Date().toISOString();\n const detailsJson = entry.details ? JSON.stringify(entry.details) : \"{}\";\n\n const entryHash = signAuditEntry(\n sequenceNumber,\n entry.action,\n entry.userId,\n entry.resource,\n detailsJson,\n timestamp,\n previousHash\n );\n\n const [log] = await db\n .insert(auditLogs)\n .values({\n userId: entry.userId,\n sessionId: entry.sessionId,\n action: entry.action,\n resource: entry.resource,\n resourceId: entry.resourceId,\n details: entry.details,\n ipAddress: entry.ipAddress,\n userAgent: entry.userAgent,\n success: entry.success ?? true,\n createdAt: new Date(timestamp),\n sequenceNumber,\n entryHash,\n previousHash,\n })\n .returning();\n\n return log.id;\n}\n\nexport interface AuditQueryOptions {\n userId?: string;\n action?: AuditAction;\n resource?: AuditResource;\n startDate?: Date;\n endDate?: Date;\n limit?: number;\n offset?: number;\n}\n\nexport async function queryAuditLogs(options: AuditQueryOptions = {}) {\n const {\n userId,\n action,\n resource,\n startDate,\n endDate,\n limit = 100,\n offset = 0,\n } = options;\n\n let query = db.select().from(auditLogs);\n\n const conditions = [];\n\n if (userId) {\n conditions.push(eq(auditLogs.userId, userId));\n }\n\n if (action) {\n conditions.push(eq(auditLogs.action, action));\n }\n\n if (resource) {\n conditions.push(eq(auditLogs.resource, resource));\n }\n\n if (startDate) {\n conditions.push(gte(auditLogs.createdAt, startDate));\n }\n\n if (endDate) {\n conditions.push(lte(auditLogs.createdAt, endDate));\n }\n\n if (conditions.length > 0) {\n query = query.where(and(...conditions)) as typeof query;\n }\n\n const logs = await query\n .orderBy(desc(auditLogs.createdAt))\n .limit(limit)\n .offset(offset);\n\n return logs;\n}\n\nexport async function getRecentUserActivity(\n userId: string,\n hours = 24\n): Promise<typeof auditLogs.$inferSelect[]> {\n const since = new Date(Date.now() - hours * 60 * 60 * 1000);\n\n return db\n .select()\n .from(auditLogs)\n .where(and(eq(auditLogs.userId, userId), gte(auditLogs.createdAt, since)))\n .orderBy(desc(auditLogs.createdAt))\n .limit(100);\n}\n\nexport async function countActionsByType(\n userId: string,\n startDate: Date,\n endDate: Date\n): Promise<Record<string, number>> {\n const logs = await db\n .select()\n .from(auditLogs)\n .where(\n and(\n eq(auditLogs.userId, userId),\n gte(auditLogs.createdAt, startDate),\n lte(auditLogs.createdAt, endDate)\n )\n );\n\n const counts: Record<string, number> = {};\n for (const log of logs) {\n counts[log.action] = (counts[log.action] || 0) + 1;\n }\n\n return counts;\n}\n\n// --- Audit chain verification (SOC 2 compliance) ---\n\nexport async function verifyAuditChain(\n options?: { fromSequence?: number; limit?: number }\n): Promise<{\n valid: boolean;\n totalChecked: number;\n firstInvalid?: number;\n errors: Array<{ sequenceNumber: number; error: string }>;\n}> {\n const fromSequence = options?.fromSequence ?? 1;\n const batchLimit = options?.limit ?? 10000;\n\n // Fetch the entry just before fromSequence to get its hash for linkage check\n let expectedPreviousHash: string | null = null;\n let expectedSequence = fromSequence;\n\n if (fromSequence > 1) {\n const [prev] = await db\n .select({\n sequenceNumber: auditLogs.sequenceNumber,\n entryHash: auditLogs.entryHash,\n })\n .from(auditLogs)\n .where(eq(auditLogs.sequenceNumber, fromSequence - 1))\n .limit(1);\n\n expectedPreviousHash = prev?.entryHash ?? null;\n }\n\n const entries = await db\n .select()\n .from(auditLogs)\n .where(gte(auditLogs.sequenceNumber, fromSequence))\n .orderBy(asc(auditLogs.sequenceNumber))\n .limit(batchLimit);\n\n const errors: Array<{ sequenceNumber: number; error: string }> = [];\n\n for (const entry of entries) {\n const seq = entry.sequenceNumber;\n\n if (seq == null) {\n errors.push({\n sequenceNumber: expectedSequence,\n error: \"Missing sequence number\",\n });\n expectedSequence++;\n continue;\n }\n\n // Check sequence continuity\n if (seq !== expectedSequence) {\n errors.push({\n sequenceNumber: expectedSequence,\n error: `Sequence gap: expected ${expectedSequence}, got ${seq}`,\n });\n expectedSequence = seq; // re-sync\n }\n\n // Check previousHash linkage\n if ((entry.previousHash ?? null) !== expectedPreviousHash) {\n errors.push({\n sequenceNumber: seq,\n error: `Previous hash mismatch: expected ${expectedPreviousHash ?? \"(null)\"}, got ${entry.previousHash ?? \"(null)\"}`,\n });\n }\n\n // Recompute and verify entryHash\n const detailsJson = entry.details ? JSON.stringify(entry.details) : \"{}\";\n const timestamp = entry.createdAt.toISOString();\n\n const recomputed = signAuditEntry(\n seq,\n entry.action,\n entry.userId ?? undefined,\n entry.resource ?? undefined,\n detailsJson,\n timestamp,\n entry.previousHash\n );\n\n if (recomputed !== entry.entryHash) {\n errors.push({\n sequenceNumber: seq,\n error: \"Entry hash mismatch — record may have been tampered with\",\n });\n }\n\n // Advance expectations\n expectedPreviousHash = entry.entryHash;\n expectedSequence = seq + 1;\n }\n\n return {\n valid: errors.length === 0,\n totalChecked: entries.length,\n firstInvalid: errors.length > 0 ? errors[0].sequenceNumber : undefined,\n errors,\n };\n}\n\nexport async function getAuditChainIntegrity(): Promise<{\n totalEntries: number;\n oldestEntry: Date | null;\n newestEntry: Date | null;\n lastVerified: number;\n chainValid: boolean;\n lastSequence: number;\n}> {\n const [stats] = await db\n .select({\n totalEntries: count(auditLogs.id),\n oldestEntry: min(auditLogs.createdAt),\n newestEntry: max(auditLogs.createdAt),\n lastSequence: max(auditLogs.sequenceNumber),\n })\n .from(auditLogs);\n\n const totalEntries = Number(stats.totalEntries ?? 0);\n const lastSequence = stats.lastSequence ?? 0;\n\n // Verify the last 1000 entries\n const verifyFrom = Math.max(1, lastSequence - 999);\n const verification = await verifyAuditChain({\n fromSequence: verifyFrom,\n limit: 1000,\n });\n\n return {\n totalEntries,\n oldestEntry: stats.oldestEntry ? new Date(stats.oldestEntry) : null,\n newestEntry: stats.newestEntry ? new Date(stats.newestEntry) : null,\n lastVerified: verification.totalChecked,\n chainValid: verification.valid,\n lastSequence,\n };\n}\n\n// Convenience functions for common audit events\nexport const audit = {\n login: (userId: string, ipAddress?: string, userAgent?: string) =>\n logAudit({\n userId,\n action: \"login\",\n resource: \"session\",\n ipAddress,\n userAgent,\n }),\n\n logout: (userId: string, sessionId: string) =>\n logAudit({\n userId,\n sessionId,\n action: \"logout\",\n resource: \"session\",\n }),\n\n toolUse: (\n userId: string,\n toolName: string,\n input: Record<string, unknown>,\n success: boolean\n ) =>\n logAudit({\n userId,\n action: \"tool_use\",\n resource: \"tool\",\n resourceId: toolName,\n details: { input },\n success,\n }),\n\n shellExecute: (\n userId: string,\n command: string,\n exitCode: number,\n durationMs: number\n ) =>\n logAudit({\n userId,\n action: \"shell_execute\",\n resource: \"shell\",\n details: { command, exitCode, durationMs },\n success: exitCode === 0,\n }),\n\n fileAccess: (\n userId: string,\n action: \"file_read\" | \"file_write\",\n filePath: string\n ) =>\n logAudit({\n userId,\n action,\n resource: \"file\",\n resourceId: filePath,\n }),\n\n memoryCreate: (userId: string, memoryId: string, memoryType: string) =>\n logAudit({\n userId,\n action: \"memory_create\",\n resource: \"memory\",\n resourceId: memoryId,\n details: { type: memoryType },\n }),\n\n modeChange: (\n userId: string,\n fromMode: string | null,\n toMode: string\n ) =>\n logAudit({\n userId,\n action: \"mode_change\",\n resource: \"mode\",\n details: { fromMode, toMode },\n }),\n\n agentSpawn: (userId: string, agentId: string, agentType: string) =>\n logAudit({\n userId,\n action: \"agent_spawn\",\n resource: \"agent\",\n resourceId: agentId,\n details: { type: agentType },\n }),\n\n error: (\n userId: string | undefined,\n errorType: string,\n message: string,\n context?: Record<string, unknown>\n ) =>\n logAudit({\n userId,\n action: \"error\",\n details: { errorType, message, ...context },\n success: false,\n }),\n};\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tools/file-generation/pdf.ts"],"sourcesContent":["import { createWriteStream } from \"fs\";\nimport { mkdir } from \"fs/promises\";\nimport { dirname, join } from \"path\";\nimport { tmpdir } from \"os\";\nimport { randomBytes } from \"crypto\";\nimport { isPathAllowed } from \"../../utils/paths\";\n\nexport interface PDFOptions {\n title?: string;\n author?: string;\n format?: \"A4\" | \"Letter\" | \"Legal\";\n orientation?: \"portrait\" | \"landscape\";\n margins?: {\n top: number;\n bottom: number;\n left: number;\n right: number;\n };\n}\n\nexport interface PDFGenerationResult {\n success: boolean;\n filePath?: string;\n error?: string;\n}\n\nconst DEFAULT_OPTIONS: PDFOptions = {\n format: \"A4\",\n orientation: \"portrait\",\n margins: { top: 72, bottom: 72, left: 72, right: 72 },\n};\n\n// Generate temp file path\nfunction getTempPath(): string {\n const id = randomBytes(8).toString(\"hex\");\n return join(tmpdir(), `sentinel-doc-${id}.pdf`);\n}\n\n// Simple markdown to text conversion (for basic PDF)\nfunction markdownToText(markdown: string): string {\n return markdown\n // Remove code blocks\n .replace(/```[\\s\\S]*?```/g, (match) => {\n const code = match.replace(/```\\w*\\n?/g, \"\").trim();\n return `\\n${code}\\n`;\n })\n // Remove inline code\n .replace(/`([^`]+)`/g, \"$1\")\n // Convert headers\n .replace(/^### (.+)$/gm, \"\\n$1\\n\" + \"-\".repeat(30))\n .replace(/^## (.+)$/gm, \"\\n$1\\n\" + \"=\".repeat(40))\n .replace(/^# (.+)$/gm, \"\\n$1\\n\" + \"=\".repeat(50))\n // Convert bold/italic\n .replace(/\\*\\*([^*]+)\\*\\*/g, \"$1\")\n .replace(/\\*([^*]+)\\*/g, \"$1\")\n .replace(/__([^_]+)__/g, \"$1\")\n .replace(/_([^_]+)_/g, \"$1\")\n // Convert links\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, \"$1\")\n // Convert lists\n .replace(/^[-*] /gm, \"• \")\n .replace(/^\\d+\\. /gm, \" \")\n // Clean up\n .replace(/\\n{3,}/g, \"\\n\\n\");\n}\n\n// Generate PDF natively using PDFKit (no browser needed)\nexport async function generatePDFNative(\n content: string,\n outputPath?: string,\n options: PDFOptions = {}\n): Promise<PDFGenerationResult> {\n const finalOptions = { ...DEFAULT_OPTIONS, ...options };\n const filePath = outputPath || getTempPath();\n\n if (outputPath && !isPathAllowed(outputPath)) {\n return { success: false, error: \"Access to this path is not allowed\" };\n }\n\n try {\n await mkdir(dirname(filePath), { recursive: true });\n\n const PDFDocument = (await import(\"pdfkit\")).default;\n const doc = new PDFDocument({\n size: finalOptions.format || \"A4\",\n layout: finalOptions.orientation || \"portrait\",\n margins: finalOptions.margins || { top: 72, bottom: 72, left: 72, right: 72 },\n info: {\n Title: finalOptions.title || \"Document\",\n Author: finalOptions.author || \"OpenSentinel\",\n },\n });\n\n const stream = createWriteStream(filePath);\n doc.pipe(stream);\n\n // Parse markdown-like content into PDF sections\n const lines = content.split(\"\\n\");\n for (const line of lines) {\n if (line.startsWith(\"# \")) {\n doc.fontSize(24).font(\"Helvetica-Bold\").text(line.slice(2), { align: \"left\" });\n doc.moveDown(0.5);\n } else if (line.startsWith(\"## \")) {\n doc.fontSize(18).font(\"Helvetica-Bold\").text(line.slice(3), { align: \"left\" });\n doc.moveDown(0.3);\n } else if (line.startsWith(\"### \")) {\n doc.fontSize(14).font(\"Helvetica-Bold\").text(line.slice(4), { align: \"left\" });\n doc.moveDown(0.2);\n } else if (line.startsWith(\"- \") || line.startsWith(\"* \")) {\n doc.fontSize(12).font(\"Helvetica\").text(` \\u2022 ${line.slice(2)}`, { indent: 20 });\n } else if (line.trim() === \"\") {\n doc.moveDown(0.5);\n } else {\n doc.fontSize(12).font(\"Helvetica\").text(line, { align: \"left\" });\n }\n }\n\n doc.end();\n await new Promise<void>((resolve, reject) => {\n stream.on(\"finish\", resolve);\n stream.on(\"error\", reject);\n });\n\n return { success: true, filePath };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\n// Generate PDF from markdown content using PDFKit\nexport async function generatePDFFromMarkdown(\n markdown: string,\n outputPath?: string,\n options: PDFOptions = {}\n): Promise<PDFGenerationResult> {\n // Use native PDFKit implementation\n return generatePDFNative(markdown, outputPath, options);\n}\n\n// Generate PDF from HTML (requires browser/puppeteer)\nexport async function generatePDFFromHTML(\n html: string,\n outputPath?: string,\n options: PDFOptions = {}\n): Promise<PDFGenerationResult> {\n const filePath = outputPath || getTempPath();\n\n // Security check\n if (outputPath && !isPathAllowed(outputPath)) {\n return {\n success: false,\n error: \"Access to this path is not allowed\",\n };\n }\n\n try {\n // Ensure directory exists\n await mkdir(dirname(filePath), { recursive: true });\n\n // Try to use Playwright if available\n try {\n const { chromium } = await import(\"playwright\");\n const browser = await chromium.launch({ headless: true });\n const page = await browser.newPage();\n\n await page.setContent(html, { waitUntil: \"networkidle\" });\n\n await page.pdf({\n path: filePath,\n format: options.format || \"A4\",\n landscape: options.orientation === \"landscape\",\n margin: options.margins\n ? {\n top: `${options.margins.top}px`,\n bottom: `${options.margins.bottom}px`,\n left: `${options.margins.left}px`,\n right: `${options.margins.right}px`,\n }\n : undefined,\n });\n\n await browser.close();\n\n return { success: true, filePath };\n } catch {\n // Fallback: save as HTML\n const { writeFile } = await import(\"fs/promises\");\n const htmlPath = filePath.replace(\".pdf\", \".html\");\n await writeFile(htmlPath, html, \"utf-8\");\n\n return {\n success: true,\n filePath: htmlPath,\n };\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\n// Main function for tool use\nexport async function generatePDF(\n content: string,\n filename: string,\n options?: PDFOptions & { contentType?: \"markdown\" | \"html\" }\n): Promise<PDFGenerationResult> {\n const outputPath = isPathAllowed(filename) ? filename : join(tmpdir(), filename);\n const contentType = options?.contentType || \"markdown\";\n\n if (contentType === \"html\") {\n return generatePDFFromHTML(content, outputPath, options);\n }\n\n return generatePDFFromMarkdown(content, outputPath, options);\n}\n\nexport default {\n generatePDF,\n generatePDFNative,\n generatePDFFromMarkdown,\n generatePDFFromHTML,\n};\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
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\";\n\nexport interface ParsedEmail {\n id: string;\n subject: string;\n from: EmailAddress[];\n to: EmailAddress[];\n cc: EmailAddress[];\n date: Date;\n body: {\n text: string;\n html: string;\n snippet: string;\n };\n thread: ThreadInfo;\n attachments: ParsedAttachment[];\n metadata: EmailMetadata;\n}\n\nexport interface ThreadInfo {\n id: string;\n messageId: string;\n inReplyTo?: string;\n references: string[];\n position: number;\n isReply: boolean;\n isForward: boolean;\n}\n\nexport interface ParsedAttachment {\n filename: string;\n contentType: string;\n size: number;\n isInline: boolean;\n cid?: string;\n content?: Buffer;\n extension: string;\n category: \"image\" | \"document\" | \"archive\" | \"media\" | \"other\";\n}\n\nexport interface EmailMetadata {\n isRead: boolean;\n isFlagged: boolean;\n isSpam: boolean;\n isDraft: boolean;\n labels: string[];\n importance: \"high\" | \"normal\" | \"low\";\n hasAttachments: boolean;\n attachmentCount: number;\n totalAttachmentSize: number;\n}\n\nexport interface EmailThread {\n id: string;\n subject: string;\n participants: EmailAddress[];\n messageCount: number;\n unreadCount: number;\n lastMessageDate: Date;\n firstMessageDate: Date;\n messages: ParsedEmail[];\n snippet: string;\n}\n\nexport interface QuotedSection {\n level: number;\n content: string;\n attribution?: string;\n}\n\nexport interface EmailBodyParts {\n newContent: string;\n quotedContent: QuotedSection[];\n signature?: string;\n}\n\n/**\n * Parse an email message into a more structured format\n */\nexport function parseEmail(email: EmailMessage): ParsedEmail {\n const isReply = email.subject.toLowerCase().startsWith(\"re:\");\n const isForward = email.subject.toLowerCase().startsWith(\"fwd:\") ||\n email.subject.toLowerCase().startsWith(\"fw:\");\n\n // Calculate attachment metadata\n const attachments = email.attachments.map(att => parseAttachment(att));\n const totalAttachmentSize = attachments.reduce((sum, att) => sum + att.size, 0);\n\n // Determine importance from headers or flags\n let importance: \"high\" | \"normal\" | \"low\" = \"normal\";\n const importanceHeader = email.headers.get(\"importance\") ||\n email.headers.get(\"x-priority\");\n if (importanceHeader) {\n const lowerHeader = importanceHeader.toLowerCase();\n if (lowerHeader.includes(\"high\") || lowerHeader === \"1\" || lowerHeader === \"2\") {\n importance = \"high\";\n } else if (lowerHeader.includes(\"low\") || lowerHeader === \"5\") {\n importance = \"low\";\n }\n }\n\n return {\n id: email.id,\n subject: email.subject,\n from: email.from,\n to: email.to,\n cc: email.cc,\n date: email.date,\n body: {\n text: email.text,\n html: email.html,\n snippet: email.snippet || email.text.substring(0, 200).replace(/\\s+/g, \" \").trim(),\n },\n thread: {\n id: email.threadId || email.messageId,\n messageId: email.messageId,\n inReplyTo: email.inReplyTo,\n references: email.references,\n position: email.references.length,\n isReply,\n isForward,\n },\n attachments,\n metadata: {\n isRead: email.flags.includes(\"\\\\Seen\"),\n isFlagged: email.flags.includes(\"\\\\Flagged\"),\n isSpam: email.labels.includes(\"Spam\") || email.labels.includes(\"Junk\"),\n isDraft: email.flags.includes(\"\\\\Draft\"),\n labels: email.labels,\n importance,\n hasAttachments: attachments.length > 0,\n attachmentCount: attachments.length,\n totalAttachmentSize,\n },\n };\n}\n\n/**\n * Parse an attachment into structured format\n */\nexport function parseAttachment(attachment: EmailAttachment): ParsedAttachment {\n const extension = attachment.filename.includes(\".\")\n ? attachment.filename.split(\".\").pop()?.toLowerCase() || \"\"\n : \"\";\n\n return {\n filename: attachment.filename,\n contentType: attachment.contentType,\n size: attachment.size,\n isInline: !!attachment.contentId,\n cid: attachment.contentId,\n content: attachment.content,\n extension,\n category: categorizeAttachment(attachment.contentType, extension),\n };\n}\n\n/**\n * Categorize an attachment by its type\n */\nfunction categorizeAttachment(\n contentType: string,\n extension: string\n): \"image\" | \"document\" | \"archive\" | \"media\" | \"other\" {\n const lowerContentType = contentType.toLowerCase();\n\n // Images\n if (lowerContentType.startsWith(\"image/\")) {\n return \"image\";\n }\n\n // Documents\n const docExtensions = [\"pdf\", \"doc\", \"docx\", \"xls\", \"xlsx\", \"ppt\", \"pptx\", \"txt\", \"rtf\", \"odt\", \"ods\", \"odp\", \"csv\"];\n const docMimeTypes = [\n \"application/pdf\",\n \"application/msword\",\n \"application/vnd.openxmlformats\",\n \"application/vnd.ms-\",\n \"text/plain\",\n \"text/csv\",\n \"application/vnd.oasis.opendocument\",\n ];\n if (docExtensions.includes(extension) || docMimeTypes.some(m => lowerContentType.includes(m))) {\n return \"document\";\n }\n\n // Archives\n const archiveExtensions = [\"zip\", \"rar\", \"7z\", \"tar\", \"gz\", \"bz2\"];\n const archiveMimeTypes = [\"application/zip\", \"application/x-rar\", \"application/x-7z\", \"application/gzip\"];\n if (archiveExtensions.includes(extension) || archiveMimeTypes.some(m => lowerContentType.includes(m))) {\n return \"archive\";\n }\n\n // Media (audio/video)\n if (lowerContentType.startsWith(\"audio/\") || lowerContentType.startsWith(\"video/\")) {\n return \"media\";\n }\n\n return \"other\";\n}\n\n/**\n * Group emails into threads\n */\nexport function groupIntoThreads(emails: EmailMessage[]): EmailThread[] {\n const threadMap = new Map<string, EmailMessage[]>();\n\n // Group by thread ID (using references chain or Message-ID)\n for (const email of emails) {\n // Find the root message ID for this thread\n let threadId = email.threadId || email.messageId;\n\n if (email.references.length > 0) {\n // Use the first reference as the thread root\n threadId = email.references[0];\n }\n\n if (!threadMap.has(threadId)) {\n threadMap.set(threadId, []);\n }\n threadMap.get(threadId)!.push(email);\n }\n\n // Convert to EmailThread objects\n const threads: EmailThread[] = [];\n\n for (const [threadId, messages] of threadMap) {\n // Sort messages by date (oldest first)\n messages.sort((a, b) => a.date.getTime() - b.date.getTime());\n\n // Collect all participants\n const participantMap = new Map<string, EmailAddress>();\n for (const msg of messages) {\n for (const addr of [...msg.from, ...msg.to, ...msg.cc]) {\n if (!participantMap.has(addr.address)) {\n participantMap.set(addr.address, addr);\n }\n }\n }\n\n // Count unread\n const unreadCount = messages.filter(m => !m.flags.includes(\"\\\\Seen\")).length;\n\n // Get the subject (use the original, non-reply subject if possible)\n let subject = messages[0].subject;\n for (const msg of messages) {\n const cleaned = cleanSubject(msg.subject);\n if (cleaned.length > 0 && !cleaned.toLowerCase().startsWith(\"re:\")) {\n subject = msg.subject;\n break;\n }\n }\n\n threads.push({\n id: threadId,\n subject: cleanSubject(subject),\n participants: Array.from(participantMap.values()),\n messageCount: messages.length,\n unreadCount,\n lastMessageDate: messages[messages.length - 1].date,\n firstMessageDate: messages[0].date,\n messages: messages.map(parseEmail),\n snippet: messages[messages.length - 1].snippet ||\n messages[messages.length - 1].text.substring(0, 200).replace(/\\s+/g, \" \").trim(),\n });\n }\n\n // Sort threads by last message date (newest first)\n threads.sort((a, b) => b.lastMessageDate.getTime() - a.lastMessageDate.getTime());\n\n return threads;\n}\n\n/**\n * Clean a subject line (remove Re:, Fwd:, etc.)\n */\nexport function cleanSubject(subject: string): string {\n return subject\n .replace(/^(Re|Fwd|Fw):\\s*/gi, \"\")\n .replace(/^\\[.*?\\]\\s*/, \"\") // Remove list prefixes like [ListName]\n .trim();\n}\n\n/**\n * Parse email body to separate new content from quoted sections\n */\nexport function parseEmailBody(text: string): EmailBodyParts {\n const lines = text.split(\"\\n\");\n const newLines: string[] = [];\n const quotedSections: QuotedSection[] = [];\n let currentQuote: { level: number; lines: string[]; attribution?: string } | null = null;\n let signature: string | undefined;\n let inSignature = false;\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n\n // Check for signature delimiter\n if (line.trim() === \"--\" || line.trim() === \"-- \") {\n inSignature = true;\n signature = lines.slice(i + 1).join(\"\\n\").trim();\n break;\n }\n\n // Check for quote markers\n const quoteMatch = line.match(/^(>+)\\s?/);\n\n if (quoteMatch) {\n const level = quoteMatch[1].length;\n const content = line.substring(quoteMatch[0].length);\n\n if (!currentQuote || currentQuote.level !== level) {\n // Save previous quote section\n if (currentQuote) {\n quotedSections.push({\n level: currentQuote.level,\n content: currentQuote.lines.join(\"\\n\"),\n attribution: currentQuote.attribution,\n });\n }\n currentQuote = { level, lines: [content] };\n } else {\n currentQuote.lines.push(content);\n }\n } else if (isAttributionLine(line)) {\n // This is an attribution line like \"On Date, Person wrote:\"\n if (currentQuote) {\n currentQuote.attribution = line;\n } else {\n // Start of a new quoted section\n currentQuote = { level: 1, lines: [], attribution: line };\n }\n } else {\n // Regular line\n if (currentQuote) {\n quotedSections.push({\n level: currentQuote.level,\n content: currentQuote.lines.join(\"\\n\"),\n attribution: currentQuote.attribution,\n });\n currentQuote = null;\n }\n newLines.push(line);\n }\n }\n\n // Don't forget the last quote section\n if (currentQuote) {\n quotedSections.push({\n level: currentQuote.level,\n content: currentQuote.lines.join(\"\\n\"),\n attribution: currentQuote.attribution,\n });\n }\n\n return {\n newContent: newLines.join(\"\\n\").trim(),\n quotedContent: quotedSections,\n signature,\n };\n}\n\n/**\n * Check if a line is an email attribution (e.g., \"On Jan 1, Person wrote:\")\n */\nfunction isAttributionLine(line: string): boolean {\n const attributionPatterns = [\n /^On .+ wrote:$/i,\n /^On .+, .+ wrote:$/i,\n /^.+ wrote:$/i,\n /^-{3,}\\s*Original Message\\s*-{3,}$/i,\n /^-{3,}\\s*Forwarded message\\s*-{3,}$/i,\n /^From:\\s*.+$/i,\n /^Sent:\\s*.+$/i,\n ];\n\n return attributionPatterns.some(pattern => pattern.test(line.trim()));\n}\n\n/**\n * Extract email addresses from a text string\n */\nexport function extractEmailAddresses(text: string): string[] {\n const emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/g;\n const matches = text.match(emailRegex) || [];\n return [...new Set(matches)];\n}\n\n/**\n * Extract URLs from email text\n */\nexport function extractUrls(text: string): string[] {\n const urlRegex = /https?:\\/\\/[^\\s<>\"\\)]+/gi;\n const matches = text.match(urlRegex) || [];\n return [...new Set(matches)];\n}\n\n/**\n * Extract phone numbers from email text\n */\nexport function extractPhoneNumbers(text: string): string[] {\n const phoneRegex = /(?:\\+\\d{1,3}[-.\\s]?)?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}/g;\n const matches = text.match(phoneRegex) || [];\n return [...new Set(matches)];\n}\n\n/**\n * Get a summary of an email suitable for display\n */\nexport function getEmailSummary(email: EmailMessage | ParsedEmail): {\n from: string;\n subject: string;\n snippet: string;\n date: string;\n attachmentCount: number;\n} {\n const fromAddr = email.from[0];\n const fromDisplay = fromAddr\n ? fromAddr.name || fromAddr.address\n : \"Unknown\";\n\n const snippet = \"body\" in email\n ? email.body.snippet\n : email.snippet || email.text.substring(0, 100);\n\n const attachmentCount = \"metadata\" in email\n ? email.metadata.attachmentCount\n : email.attachments.length;\n\n return {\n from: fromDisplay,\n subject: email.subject,\n snippet,\n date: formatEmailDate(email.date),\n attachmentCount,\n };\n}\n\n/**\n * Format a date for email display\n */\nfunction formatEmailDate(date: Date): string {\n const now = new Date();\n const diff = now.getTime() - date.getTime();\n const days = Math.floor(diff / (1000 * 60 * 60 * 24));\n\n if (days === 0) {\n // Today - show time\n return date.toLocaleTimeString(\"en-US\", {\n hour: \"numeric\",\n minute: \"2-digit\",\n hour12: true,\n });\n } else if (days === 1) {\n return \"Yesterday\";\n } else if (days < 7) {\n // This week - show day name\n return date.toLocaleDateString(\"en-US\", { weekday: \"short\" });\n } else if (date.getFullYear() === now.getFullYear()) {\n // This year - show month and day\n return date.toLocaleDateString(\"en-US\", { month: \"short\", day: \"numeric\" });\n } else {\n // Older - show full date\n return date.toLocaleDateString(\"en-US\", {\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n });\n }\n}\n\n/**\n * Calculate the size of an email in bytes\n */\nexport function calculateEmailSize(email: EmailMessage): number {\n let size = 0;\n\n // Text content\n size += new TextEncoder().encode(email.text).length;\n size += new TextEncoder().encode(email.html).length;\n\n // Headers (estimate)\n size += new TextEncoder().encode(email.subject).length;\n size += email.from.reduce((s, a) => s + (a.name?.length || 0) + a.address.length + 10, 0);\n size += email.to.reduce((s, a) => s + (a.name?.length || 0) + a.address.length + 10, 0);\n\n // Attachments\n size += email.attachments.reduce((s, a) => s + a.size, 0);\n\n return size;\n}\n\n/**\n * Check if an email matches a filter\n */\nexport function matchesFilter(\n email: EmailMessage | ParsedEmail,\n filter: {\n from?: string | RegExp;\n to?: string | RegExp;\n subject?: string | RegExp;\n body?: string | RegExp;\n hasAttachments?: boolean;\n isUnread?: boolean;\n isFlagged?: boolean;\n dateAfter?: Date;\n dateBefore?: Date;\n }\n): boolean {\n const parsed = \"metadata\" in email ? email : parseEmail(email);\n\n // Check from\n if (filter.from) {\n const fromMatch = parsed.from.some(addr => {\n const str = `${addr.name} ${addr.address}`;\n return filter.from instanceof RegExp\n ? filter.from.test(str)\n : str.toLowerCase().includes(filter.from.toLowerCase());\n });\n if (!fromMatch) return false;\n }\n\n // Check to\n if (filter.to) {\n const toMatch = parsed.to.some(addr => {\n const str = `${addr.name} ${addr.address}`;\n return filter.to instanceof RegExp\n ? filter.to.test(str)\n : str.toLowerCase().includes(filter.to.toLowerCase());\n });\n if (!toMatch) return false;\n }\n\n // Check subject\n if (filter.subject) {\n const subjectMatch = filter.subject instanceof RegExp\n ? filter.subject.test(parsed.subject)\n : parsed.subject.toLowerCase().includes(filter.subject.toLowerCase());\n if (!subjectMatch) return false;\n }\n\n // Check body\n if (filter.body) {\n const bodyText = parsed.body.text;\n const bodyMatch = filter.body instanceof RegExp\n ? filter.body.test(bodyText)\n : bodyText.toLowerCase().includes(filter.body.toLowerCase());\n if (!bodyMatch) return false;\n }\n\n // Check attachments\n if (filter.hasAttachments !== undefined) {\n if (parsed.metadata.hasAttachments !== filter.hasAttachments) return false;\n }\n\n // Check read status\n if (filter.isUnread !== undefined) {\n if (parsed.metadata.isRead === filter.isUnread) return false;\n }\n\n // Check flagged status\n if (filter.isFlagged !== undefined) {\n if (parsed.metadata.isFlagged !== filter.isFlagged) return false;\n }\n\n // Check date range\n if (filter.dateAfter && parsed.date < filter.dateAfter) return false;\n if (filter.dateBefore && parsed.date > filter.dateBefore) return false;\n\n return true;\n}\n\nexport default {\n parseEmail,\n parseAttachment,\n groupIntoThreads,\n cleanSubject,\n parseEmailBody,\n extractEmailAddresses,\n extractUrls,\n extractPhoneNumbers,\n getEmailSummary,\n calculateEmailSize,\n matchesFilter,\n};\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":[]}
|