opensentinel 3.1.1 → 3.6.1
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 +126 -83
- package/dist/agent-manager-7N7REQZQ.js +39 -0
- package/dist/agent-processor-I23VWQY3.js +280 -0
- package/dist/agent-processor-I23VWQY3.js.map +1 -0
- package/dist/agent-types-2T4PXLFQ.js +12 -0
- package/dist/alerting-4I37GG4U.js +699 -0
- package/dist/alerting-4I37GG4U.js.map +1 -0
- package/dist/analysis-agent-JWN2GXYE.js +288 -0
- package/dist/analysis-agent-JWN2GXYE.js.map +1 -0
- package/dist/{archiver-AVNBYCKQ.js → archiver-XLRIIXPY.js} +86 -17
- package/dist/archiver-XLRIIXPY.js.map +1 -0
- package/dist/{audit-logger-OBPR7CRO.js → audit-logger-AU3TMWKI.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-MU2TJQ3Y.js +46 -0
- package/dist/brain-SLA474EU.js +65 -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-6PMVAAA7.js → chunk-2RGPWU77.js} +3 -3
- package/dist/{chunk-TVEWKIK3.js → chunk-2WTKTG2C.js} +2 -2
- package/dist/{chunk-MXAPLSJ5.js → chunk-45YXODSB.js} +2 -2
- package/dist/{chunk-SJSUSJ47.js → chunk-4YJRBMMA.js} +2 -2
- package/dist/chunk-643M3AP5.js +564 -0
- package/dist/chunk-643M3AP5.js.map +1 -0
- package/dist/{chunk-766ASQWE.js → chunk-6JY4HNUH.js} +2413 -2368
- package/dist/chunk-6JY4HNUH.js.map +1 -0
- package/dist/chunk-6LTLIYAQ.js +194 -0
- package/dist/chunk-6LTLIYAQ.js.map +1 -0
- package/dist/chunk-6UZPE35A.js +724 -0
- package/dist/chunk-6UZPE35A.js.map +1 -0
- package/dist/chunk-6W6PTJFT.js +181 -0
- package/dist/chunk-6W6PTJFT.js.map +1 -0
- package/dist/chunk-7MZN73J2.js +162 -0
- package/dist/chunk-7MZN73J2.js.map +1 -0
- package/dist/{chunk-SVAPX2XN.js → chunk-A24GPVLY.js} +9 -7
- package/dist/{chunk-SVAPX2XN.js.map → chunk-A24GPVLY.js.map} +1 -1
- package/dist/chunk-AD6YEH6U.js +3408 -0
- package/dist/chunk-AD6YEH6U.js.map +1 -0
- package/dist/chunk-ADTDYJO7.js +265 -0
- package/dist/chunk-ADTDYJO7.js.map +1 -0
- package/dist/{chunk-WRAKK6K6.js → chunk-AR34B6XR.js} +5 -3
- package/dist/{chunk-WRAKK6K6.js.map → chunk-AR34B6XR.js.map} +1 -1
- package/dist/chunk-BMOUYXLX.js +418 -0
- package/dist/chunk-BMOUYXLX.js.map +1 -0
- 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-RZ4YESBG.js → chunk-DOYGMNMK.js} +1 -1
- package/dist/chunk-DOYGMNMK.js.map +1 -0
- package/dist/chunk-FFV2SXFD.js +380 -0
- package/dist/chunk-FFV2SXFD.js.map +1 -0
- package/dist/{chunk-EVE7MIIY.js → chunk-GUKKW7JI.js} +15 -16
- package/dist/chunk-GUKKW7JI.js.map +1 -0
- package/dist/{chunk-66OJ3WB4.js → chunk-H3BOLSTS.js} +2 -2
- package/dist/chunk-HKOPRRDJ.js +1021 -0
- package/dist/chunk-HKOPRRDJ.js.map +1 -0
- package/dist/{chunk-BXZ6EA52.js → chunk-HTF2GIQC.js} +57 -3
- package/dist/chunk-HTF2GIQC.js.map +1 -0
- package/dist/{chunk-TYAGMJNV.js → chunk-JOA5A3G3.js} +5 -5
- package/dist/{chunk-OCVQGBJK.js → chunk-KABG5PG3.js} +6 -4
- package/dist/{chunk-OCVQGBJK.js.map → chunk-KABG5PG3.js.map} +1 -1
- package/dist/{chunk-VEHFVBLI.js → chunk-KT7NLIXP.js} +2 -2
- package/dist/chunk-LFDXEYYB.js +150 -0
- package/dist/chunk-LFDXEYYB.js.map +1 -0
- package/dist/{chunk-I6BDYQIG.js → chunk-M7YLQHFP.js} +6 -6
- package/dist/chunk-M7YLQHFP.js.map +1 -0
- package/dist/{chunk-AYUKPTSM.js → chunk-MFK34XSY.js} +96 -218
- package/dist/chunk-MFK34XSY.js.map +1 -0
- package/dist/chunk-MIC5IBQF.js +386 -0
- package/dist/chunk-MIC5IBQF.js.map +1 -0
- package/dist/{chunk-4UOE5TUZ.js → chunk-NMSHVO5O.js} +4 -4
- package/dist/{chunk-XKYRH4FM.js → chunk-NYVBXUGD.js} +13 -32
- package/dist/chunk-NYVBXUGD.js.map +1 -0
- package/dist/chunk-ODCFS5WD.js +463 -0
- package/dist/chunk-ODCFS5WD.js.map +1 -0
- package/dist/{chunk-ZLZKF2PM.js → chunk-PUNIMPMY.js} +32 -2
- package/dist/chunk-PUNIMPMY.js.map +1 -0
- package/dist/chunk-S4NJJS5C.js +37 -0
- package/dist/chunk-S4NJJS5C.js.map +1 -0
- package/dist/{chunk-NHMBTUMW.js → chunk-TAAZB5KN.js} +2 -2
- 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-4GLYY4NN.js → chunk-UWUIJTT4.js} +8 -2
- package/dist/chunk-UWUIJTT4.js.map +1 -0
- package/dist/{chunk-SPPMCAKG.js → chunk-VKMFUIVA.js} +2 -2
- package/dist/chunk-VKMFUIVA.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-ZIYTHUM5.js +457 -0
- package/dist/chunk-ZIYTHUM5.js.map +1 -0
- package/dist/chunker-K6WTR62A.js +12 -0
- package/dist/cli.js +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 +1 -1
- package/dist/commands/start.js +2 -2
- package/dist/commands/status.js +1 -1
- package/dist/commands/stop.js +1 -1
- package/dist/commands/utils.js +1 -1
- package/dist/cost-tracker-EMOIOYH7.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-LRIOKQBO.js +77 -0
- package/dist/discord-NKR3X4AV.js +80 -0
- package/dist/documents-EYIYLZK2.js +184 -0
- package/dist/documents-EYIYLZK2.js.map +1 -0
- package/dist/docx-parser-EXL4TN5E.js +16 -0
- package/dist/{email-K7LO2IPB.js → email-EAQNULVD.js} +33 -25
- package/dist/{email-K7LO2IPB.js.map → email-EAQNULVD.js.map} +1 -1
- package/dist/{enhanced-retrieval-DNLLEM4Z.js → enhanced-retrieval-OGHT6TS5.js} +11 -8
- package/dist/{enhanced-retrieval-DNLLEM4Z.js.map → enhanced-retrieval-OGHT6TS5.js.map} +1 -1
- package/dist/enrichment-pipeline-CMUVBDC7.js +14 -0
- package/dist/{entity-resolution-Y3IUWEAT.js → entity-resolution-4X4JU43O.js} +6 -5
- package/dist/env-CHOFICED.js +12 -0
- package/dist/error-tracker-SVQSDQDW.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-KGNILDWJ.js +833 -0
- package/dist/github-KGNILDWJ.js.map +1 -0
- package/dist/{google-workspace-DKWUVNGC.js → google-workspace-TSZPZK5G.js} +2 -2
- 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-V2XNDDHT.js +43 -0
- package/dist/inbox-summarizer-DKKRYXDR.js +55 -0
- package/dist/{incident-response-C5J7Q6DT.js → incident-response-ZTIKUWEO.js} +8 -6
- package/dist/{incident-response-C5J7Q6DT.js.map → incident-response-ZTIKUWEO.js.map} +1 -1
- 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-J7PJ7MZ3.js +46 -0
- package/dist/lib.d.ts +73 -1
- package/dist/lib.js +86 -76
- package/dist/lib.js.map +1 -1
- package/dist/{mailchimp-KKNF6QJ7.js → mailchimp-ZFYDC44J.js} +2 -2
- package/dist/{matrix-QVHG76I7.js → matrix-XHTR53VQ.js} +29 -21
- package/dist/{matrix-QVHG76I7.js.map → matrix-XHTR53VQ.js.map} +1 -1
- package/dist/{mcp-3JI6W7ZE.js → mcp-3C2TN67D.js} +3 -3
- package/dist/metrics-VJDWQWU7.js +25 -0
- package/dist/{microsoft365-UCBKJHNX.js → microsoft365-6G2IJMWC.js} +2 -2
- package/dist/multi-user-S56GUD6L.js +411 -0
- package/dist/multi-user-S56GUD6L.js.map +1 -0
- package/dist/{ocr-AC7NPX33.js → ocr-LGUIPKVZ.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-HXTAMGHT.js} +3 -3
- package/dist/presentations-HXTAMGHT.js.map +1 -0
- package/dist/{prometheus-JNT2BD4L.js → prometheus-YETCZO4I.js} +2 -2
- package/dist/{providers-J4LYPHDR.js → providers-H6YIC3MG.js} +6 -4
- package/dist/{qr-code-WIX4PB4U.js → qr-code-6WZJHRKL.js} +2 -2
- 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-CA5UNHZV.js +73 -0
- package/dist/scheduler-CA5UNHZV.js.map +1 -0
- package/dist/schema-ALJ67YVG.js +72 -0
- package/dist/schema-ALJ67YVG.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/{shopify-NCXYJB4R.js → shopify-ON2PAU27.js} +2 -2
- package/dist/signal-X7IQJGRQ.js +43 -0
- package/dist/signal-X7IQJGRQ.js.map +1 -0
- package/dist/slack-P2LFUJUQ.js +85 -0
- package/dist/slack-P2LFUJUQ.js.map +1 -0
- package/dist/{sms-M3JIOTCW.js → sms-4VME2HUL.js} +4 -4
- package/dist/sms-4VME2HUL.js.map +1 -0
- package/dist/{src-VYUE6LRA.js → src-S5KX4YEV.js} +179 -48
- package/dist/src-S5KX4YEV.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/tools-FGPN522P.js +46 -0
- package/dist/tools-FGPN522P.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-KRPQ4YUX.js +43 -0
- package/dist/whatsapp-KRPQ4YUX.js.map +1 -0
- package/dist/{word-document-7B6SJMAY.js → word-document-D6N2C47N.js} +4 -4
- package/dist/word-document-D6N2C47N.js.map +1 -0
- package/dist/workflow-store-ZYAYE5P6.js +373 -0
- package/dist/workflow-store-ZYAYE5P6.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/package.json +11 -1
- package/dist/archiver-AVNBYCKQ.js.map +0 -1
- 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-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-XKYRH4FM.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/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/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-7N7REQZQ.js.map} +0 -0
- /package/dist/{auth-UOX5K2BE.js.map → agent-types-2T4PXLFQ.js.map} +0 -0
- /package/dist/{backup-restore-PZ7CYYB7.js.map → audit-logger-AU3TMWKI.js.map} +0 -0
- /package/dist/{blocks-R3PODY47.js.map → auth-PH5IHISW.js.map} +0 -0
- /package/dist/{aws-s3-Q4LLZZPD.js.map → aws-s3-QZMURYXB.js.map} +0 -0
- /package/dist/{bot-QRARP4UN.js.map → backup-restore-72OQTZO3.js.map} +0 -0
- /package/dist/{brain-7XLLM3KC.js.map → blocks-YOWOESDD.js.map} +0 -0
- /package/dist/{chunk-PLDDJCW6.js.map → bot-MU2TJQ3Y.js.map} +0 -0
- /package/dist/{client-ZQSFPMOB.js.map → brain-SLA474EU.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-6PMVAAA7.js.map → chunk-2RGPWU77.js.map} +0 -0
- /package/dist/{chunk-TVEWKIK3.js.map → chunk-2WTKTG2C.js.map} +0 -0
- /package/dist/{chunk-MXAPLSJ5.js.map → chunk-45YXODSB.js.map} +0 -0
- /package/dist/{chunk-SJSUSJ47.js.map → chunk-4YJRBMMA.js.map} +0 -0
- /package/dist/{chunk-MQJ2ECQT.js.map → chunk-CUPEENUY.js.map} +0 -0
- /package/dist/{chunk-66OJ3WB4.js.map → chunk-H3BOLSTS.js.map} +0 -0
- /package/dist/{chunk-TYAGMJNV.js.map → chunk-JOA5A3G3.js.map} +0 -0
- /package/dist/{chunk-VEHFVBLI.js.map → chunk-KT7NLIXP.js.map} +0 -0
- /package/dist/{chunk-4UOE5TUZ.js.map → chunk-NMSHVO5O.js.map} +0 -0
- /package/dist/{chunk-NHMBTUMW.js.map → chunk-TAAZB5KN.js.map} +0 -0
- /package/dist/{clipboard-manager-TEO2GEDN.js.map → chunk-UP2VWCW5.js.map} +0 -0
- /package/dist/{cron-explain-HHQKPD3M.js.map → chunker-K6WTR62A.js.map} +0 -0
- /package/dist/{crypto-4AP47IKC.js.map → client-FOIYPOZQ.js.map} +0 -0
- /package/dist/{databases-37X4CI2Y.js.map → clipboard-manager-4SBNESGZ.js.map} +0 -0
- /package/dist/{discord-B3HUPGQ6.js.map → cost-tracker-EMOIOYH7.js.map} +0 -0
- /package/dist/{enrichment-pipeline-MNHNW65K.js.map → cron-explain-UOOOYWZZ.js.map} +0 -0
- /package/dist/{entity-resolution-Y3IUWEAT.js.map → crypto-2VG3RJR2.js.map} +0 -0
- /package/dist/{env-IWXUVTCB.js.map → databases-XDPMG5AV.js.map} +0 -0
- /package/dist/{hash-tool-ULQYD7B5.js.map → db-LRIOKQBO.js.map} +0 -0
- /package/dist/{heartbeat-monitor-GCISLXI3.js.map → discord-NKR3X4AV.js.map} +0 -0
- /package/dist/{imessage-NGA2XF2V.js.map → docx-parser-EXL4TN5E.js.map} +0 -0
- /package/dist/{inbox-summarizer-NRI4S7IF.js.map → enrichment-pipeline-CMUVBDC7.js.map} +0 -0
- /package/dist/{inventory-manager-352OHXWD.js.map → entity-resolution-4X4JU43O.js.map} +0 -0
- /package/dist/{json-tool-QE2SYHEG.js.map → env-CHOFICED.js.map} +0 -0
- /package/dist/{key-rotation-DPHU4ZTB.js.map → error-tracker-SVQSDQDW.js.map} +0 -0
- /package/dist/{google-workspace-DKWUVNGC.js.map → google-workspace-TSZPZK5G.js.map} +0 -0
- /package/dist/{mcp-3JI6W7ZE.js.map → hash-tool-ENAB5LWH.js.map} +0 -0
- /package/dist/{ocr-AC7NPX33.js.map → heartbeat-monitor-KRDYTDBF.js.map} +0 -0
- /package/dist/{ollama-BOAMSPLJ.js.map → hooks-N4MIFBVM.js.map} +0 -0
- /package/dist/{image-generation-OSU7FP6F.js.map → image-generation-MDE6AVQO.js.map} +0 -0
- /package/dist/{pages-MI523RB7.js.map → imessage-V2XNDDHT.js.map} +0 -0
- /package/dist/{pairing-IFQYCPNS.js.map → inbox-summarizer-DKKRYXDR.js.map} +0 -0
- /package/dist/{pdf-ALQVOEJR.js.map → inventory-manager-C67BSZM6.js.map} +0 -0
- /package/dist/{jira-GSGDBMIG.js.map → jira-PAGZWUBJ.js.map} +0 -0
- /package/dist/{prometheus-JNT2BD4L.js.map → json-tool-4FK5RNER.js.map} +0 -0
- /package/dist/{providers-J4LYPHDR.js.map → key-rotation-WCC5FOYS.js.map} +0 -0
- /package/dist/{qr-code-WIX4PB4U.js.map → knowledge-base-J7PJ7MZ3.js.map} +0 -0
- /package/dist/{mailchimp-KKNF6QJ7.js.map → mailchimp-ZFYDC44J.js.map} +0 -0
- /package/dist/{regex-tool-W4ABRKGK.js.map → mcp-3C2TN67D.js.map} +0 -0
- /package/dist/{scheduler-VK4WFERV.js.map → metrics-VJDWQWU7.js.map} +0 -0
- /package/dist/{microsoft365-UCBKJHNX.js.map → microsoft365-6G2IJMWC.js.map} +0 -0
- /package/dist/{search-BCLBO5E3.js.map → ocr-LGUIPKVZ.js.map} +0 -0
- /package/dist/{signal-6CGDFYL2.js.map → ollama-J7CU45WT.js.map} +0 -0
- /package/dist/{slack-IZQWIKOH.js.map → pages-XDE7JRCA.js.map} +0 -0
- /package/dist/{pair-JDFTERIK.js.map → pair-YZJFQUU5.js.map} +0 -0
- /package/dist/{sms-M3JIOTCW.js.map → pairing-77N47RAT.js.map} +0 -0
- /package/dist/{stocks-XXWBPOCU.js.map → pdf-67HGXCFJ.js.map} +0 -0
- /package/dist/{text-transform-6SGUA5Z4.js.map → pdf-parser-YLMTTYHL.js.map} +0 -0
- /package/dist/{tools-2RLEI2N6.js.map → prometheus-YETCZO4I.js.map} +0 -0
- /package/dist/{unit-converter-ZYXMEZOE.js.map → providers-H6YIC3MG.js.map} +0 -0
- /package/dist/{whatsapp-LFX6YKCM.js.map → qr-code-6WZJHRKL.js.map} +0 -0
- /package/dist/{quickbooks-XB4NII2S.js.map → quickbooks-N675W7IK.js.map} +0 -0
- /package/dist/{sendgrid-RNXCAFKM.js.map → sendgrid-QGJIVPWV.js.map} +0 -0
- /package/dist/{shopify-NCXYJB4R.js.map → shopify-ON2PAU27.js.map} +0 -0
- /package/dist/{twilio-53GEW5JT.js.map → twilio-3L7DUNYQ.js.map} +0 -0
- /package/dist/{xero-QYO66D45.js.map → xero-UHAHVYSD.js.map} +0 -0
- /package/dist/{zapier-webhook-TBZ5YF2A.js.map → zapier-webhook-NIELLTXR.js.map} +0 -0
|
@@ -0,0 +1,3408 @@
|
|
|
1
|
+
import {
|
|
2
|
+
textToSpeech
|
|
3
|
+
} from "./chunk-45YXODSB.js";
|
|
4
|
+
import {
|
|
5
|
+
transcribeAudio
|
|
6
|
+
} from "./chunk-4YJRBMMA.js";
|
|
7
|
+
import {
|
|
8
|
+
getErrorStats,
|
|
9
|
+
getRecentErrors
|
|
10
|
+
} from "./chunk-7MZN73J2.js";
|
|
11
|
+
import {
|
|
12
|
+
ImapClient,
|
|
13
|
+
SmtpClient,
|
|
14
|
+
TOOLS,
|
|
15
|
+
chat,
|
|
16
|
+
chatWithTools,
|
|
17
|
+
executeTool,
|
|
18
|
+
getAppProfile,
|
|
19
|
+
getDownloadEntry,
|
|
20
|
+
init_imap_client,
|
|
21
|
+
init_smtp_client,
|
|
22
|
+
prioritizeTools
|
|
23
|
+
} from "./chunk-6JY4HNUH.js";
|
|
24
|
+
import {
|
|
25
|
+
decryptField,
|
|
26
|
+
deleteMemory,
|
|
27
|
+
exportMemories,
|
|
28
|
+
getMemoryById,
|
|
29
|
+
searchMemories,
|
|
30
|
+
storeMemory,
|
|
31
|
+
updateMemory
|
|
32
|
+
} from "./chunk-6UZPE35A.js";
|
|
33
|
+
import {
|
|
34
|
+
brainTelemetry
|
|
35
|
+
} from "./chunk-643M3AP5.js";
|
|
36
|
+
import {
|
|
37
|
+
flushMetrics,
|
|
38
|
+
getMetricAggregates,
|
|
39
|
+
getMetricTimeSeries,
|
|
40
|
+
recordMetric
|
|
41
|
+
} from "./chunk-6LTLIYAQ.js";
|
|
42
|
+
import {
|
|
43
|
+
costTracker
|
|
44
|
+
} from "./chunk-BMOUYXLX.js";
|
|
45
|
+
import {
|
|
46
|
+
createPublicRecords
|
|
47
|
+
} from "./chunk-A24GPVLY.js";
|
|
48
|
+
import {
|
|
49
|
+
resolveEntity
|
|
50
|
+
} from "./chunk-AR34B6XR.js";
|
|
51
|
+
import {
|
|
52
|
+
audit,
|
|
53
|
+
getAuditChainIntegrity,
|
|
54
|
+
logAudit,
|
|
55
|
+
queryAuditLogs
|
|
56
|
+
} from "./chunk-KABG5PG3.js";
|
|
57
|
+
import {
|
|
58
|
+
db
|
|
59
|
+
} from "./chunk-S4NJJS5C.js";
|
|
60
|
+
import {
|
|
61
|
+
env
|
|
62
|
+
} from "./chunk-PUNIMPMY.js";
|
|
63
|
+
import {
|
|
64
|
+
apiKeys,
|
|
65
|
+
conversations,
|
|
66
|
+
graphEntities,
|
|
67
|
+
graphRelationships,
|
|
68
|
+
memories,
|
|
69
|
+
messages,
|
|
70
|
+
sessions
|
|
71
|
+
} from "./chunk-NYVBXUGD.js";
|
|
72
|
+
import {
|
|
73
|
+
__require
|
|
74
|
+
} from "./chunk-UP2VWCW5.js";
|
|
75
|
+
|
|
76
|
+
// src/inputs/api/server.ts
|
|
77
|
+
import { Hono as Hono14 } from "hono";
|
|
78
|
+
import { cors } from "hono/cors";
|
|
79
|
+
import { logger } from "hono/logger";
|
|
80
|
+
import { desc as desc2, eq as eq4 } from "drizzle-orm";
|
|
81
|
+
|
|
82
|
+
// src/core/security/api-key-manager.ts
|
|
83
|
+
import { eq, and, isNull } from "drizzle-orm";
|
|
84
|
+
import { randomBytes, createHash } from "crypto";
|
|
85
|
+
var KEY_PREFIX = "mb_";
|
|
86
|
+
function hashApiKey(key) {
|
|
87
|
+
return createHash("sha256").update(key).digest("hex");
|
|
88
|
+
}
|
|
89
|
+
async function validateApiKey(rawKey) {
|
|
90
|
+
if (!rawKey.startsWith(KEY_PREFIX)) {
|
|
91
|
+
return { valid: false };
|
|
92
|
+
}
|
|
93
|
+
const keyHash = hashApiKey(rawKey);
|
|
94
|
+
const now = /* @__PURE__ */ new Date();
|
|
95
|
+
const [key] = await db.select().from(apiKeys).where(
|
|
96
|
+
and(
|
|
97
|
+
eq(apiKeys.keyHash, keyHash),
|
|
98
|
+
isNull(apiKeys.revokedAt)
|
|
99
|
+
)
|
|
100
|
+
).limit(1);
|
|
101
|
+
if (!key) {
|
|
102
|
+
return { valid: false };
|
|
103
|
+
}
|
|
104
|
+
if (key.expiresAt && key.expiresAt < now) {
|
|
105
|
+
return { valid: false };
|
|
106
|
+
}
|
|
107
|
+
await db.update(apiKeys).set({ lastUsedAt: now }).where(eq(apiKeys.id, key.id));
|
|
108
|
+
return {
|
|
109
|
+
valid: true,
|
|
110
|
+
apiKey: {
|
|
111
|
+
id: key.id,
|
|
112
|
+
name: key.name,
|
|
113
|
+
prefix: key.keyPrefix,
|
|
114
|
+
permissions: key.permissions || [],
|
|
115
|
+
createdAt: key.createdAt,
|
|
116
|
+
lastUsedAt: now,
|
|
117
|
+
expiresAt: key.expiresAt,
|
|
118
|
+
isRevoked: false
|
|
119
|
+
},
|
|
120
|
+
userId: key.userId
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function hasPermission(apiKeyPermissions, requiredPermission) {
|
|
124
|
+
if (apiKeyPermissions.includes("*")) {
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
return apiKeyPermissions.includes(requiredPermission);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// src/core/security/session-manager.ts
|
|
131
|
+
import { eq as eq2, and as and2, gt, lt } from "drizzle-orm";
|
|
132
|
+
import { randomBytes as randomBytes2, createHash as createHash2 } from "crypto";
|
|
133
|
+
var SESSION_DURATION_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
134
|
+
function hashToken(token) {
|
|
135
|
+
return createHash2("sha256").update(token).digest("hex");
|
|
136
|
+
}
|
|
137
|
+
async function validateSession(token) {
|
|
138
|
+
const tokenHash = hashToken(token);
|
|
139
|
+
const now = /* @__PURE__ */ new Date();
|
|
140
|
+
const [session] = await db.select().from(sessions).where(and2(eq2(sessions.token, tokenHash), gt(sessions.expiresAt, now))).limit(1);
|
|
141
|
+
if (!session) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
await db.update(sessions).set({ lastActiveAt: now }).where(eq2(sessions.id, session.id));
|
|
145
|
+
return {
|
|
146
|
+
userId: session.userId,
|
|
147
|
+
sessionId: session.id,
|
|
148
|
+
expiresAt: session.expiresAt,
|
|
149
|
+
deviceInfo: session.deviceInfo
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// src/core/security/gateway-utils.ts
|
|
154
|
+
function getGatewayToken() {
|
|
155
|
+
const token = env.GATEWAY_TOKEN;
|
|
156
|
+
return token || void 0;
|
|
157
|
+
}
|
|
158
|
+
function timingSafeEqual(a, b) {
|
|
159
|
+
if (a.length !== b.length) {
|
|
160
|
+
let result2 = 1;
|
|
161
|
+
for (let i = 0; i < b.length; i++) {
|
|
162
|
+
result2 |= b.charCodeAt(i) ^ (a.charCodeAt(i % (a.length || 1)) || 0);
|
|
163
|
+
}
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
let result = 0;
|
|
167
|
+
for (let i = 0; i < a.length; i++) {
|
|
168
|
+
result |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
169
|
+
}
|
|
170
|
+
return result === 0;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// src/core/security/auth-middleware.ts
|
|
174
|
+
var PUBLIC_ROUTES = ["/health", "/api/system/status", "/api/pair", "/api/sdk/register"];
|
|
175
|
+
var SDK_PREFIX = "/api/sdk/";
|
|
176
|
+
function authMiddleware() {
|
|
177
|
+
return async (c, next) => {
|
|
178
|
+
const path = c.req.path;
|
|
179
|
+
if (PUBLIC_ROUTES.includes(path)) {
|
|
180
|
+
return next();
|
|
181
|
+
}
|
|
182
|
+
if (path.startsWith(SDK_PREFIX) && path !== "/api/sdk/register") {
|
|
183
|
+
return next();
|
|
184
|
+
}
|
|
185
|
+
const gatewayToken = getGatewayToken();
|
|
186
|
+
if (!gatewayToken) {
|
|
187
|
+
c.set("userId", "local");
|
|
188
|
+
c.set("permissions", ["*"]);
|
|
189
|
+
c.set("authMethod", "none");
|
|
190
|
+
return next();
|
|
191
|
+
}
|
|
192
|
+
const authHeader = c.req.header("Authorization");
|
|
193
|
+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
194
|
+
return c.json({ error: "Missing or invalid authorization header" }, 401);
|
|
195
|
+
}
|
|
196
|
+
const token = authHeader.slice(7);
|
|
197
|
+
if (timingSafeEqual(token, gatewayToken)) {
|
|
198
|
+
c.set("userId", "gateway");
|
|
199
|
+
c.set("permissions", ["*"]);
|
|
200
|
+
c.set("authMethod", "gateway");
|
|
201
|
+
logAudit({
|
|
202
|
+
userId: "gateway",
|
|
203
|
+
action: "login",
|
|
204
|
+
resource: "session",
|
|
205
|
+
details: { method: "gateway_token", path }
|
|
206
|
+
}).catch(() => {
|
|
207
|
+
});
|
|
208
|
+
return next();
|
|
209
|
+
}
|
|
210
|
+
const sessionInfo = await validateSession(token);
|
|
211
|
+
if (sessionInfo) {
|
|
212
|
+
c.set("userId", sessionInfo.userId);
|
|
213
|
+
c.set("permissions", ["*"]);
|
|
214
|
+
c.set("authMethod", "session");
|
|
215
|
+
logAudit({
|
|
216
|
+
userId: sessionInfo.userId,
|
|
217
|
+
action: "login",
|
|
218
|
+
resource: "session",
|
|
219
|
+
details: { method: "session", path }
|
|
220
|
+
}).catch(() => {
|
|
221
|
+
});
|
|
222
|
+
return next();
|
|
223
|
+
}
|
|
224
|
+
const apiKeyResult = await validateApiKey(token);
|
|
225
|
+
if (apiKeyResult.valid && apiKeyResult.apiKey && apiKeyResult.userId) {
|
|
226
|
+
c.set("userId", apiKeyResult.userId);
|
|
227
|
+
c.set("permissions", apiKeyResult.apiKey.permissions);
|
|
228
|
+
c.set("authMethod", "api_key");
|
|
229
|
+
logAudit({
|
|
230
|
+
userId: apiKeyResult.userId,
|
|
231
|
+
action: "login",
|
|
232
|
+
resource: "api_key",
|
|
233
|
+
details: { method: "api_key", keyId: apiKeyResult.apiKey.id, path }
|
|
234
|
+
}).catch(() => {
|
|
235
|
+
});
|
|
236
|
+
return next();
|
|
237
|
+
}
|
|
238
|
+
return c.json({ error: "Invalid or expired credentials" }, 401);
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
function requirePermission(...requiredPermissions) {
|
|
242
|
+
return async (c, next) => {
|
|
243
|
+
const permissions = c.get("permissions");
|
|
244
|
+
if (!permissions) {
|
|
245
|
+
return c.json({ error: "Not authenticated" }, 401);
|
|
246
|
+
}
|
|
247
|
+
if (permissions.includes("*")) {
|
|
248
|
+
return next();
|
|
249
|
+
}
|
|
250
|
+
for (const required of requiredPermissions) {
|
|
251
|
+
if (!hasPermission(permissions, required)) {
|
|
252
|
+
return c.json({ error: "Insufficient permissions" }, 403);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return next();
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
function getAuthUserId(c) {
|
|
259
|
+
return c.get("userId");
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// src/inputs/api/routes/osint.ts
|
|
263
|
+
import { Hono } from "hono";
|
|
264
|
+
import { eq as eq3, sql, desc, ilike, and as and3 } from "drizzle-orm";
|
|
265
|
+
var _publicRecords = null;
|
|
266
|
+
function getPublicRecords() {
|
|
267
|
+
if (!_publicRecords) _publicRecords = createPublicRecords();
|
|
268
|
+
return _publicRecords;
|
|
269
|
+
}
|
|
270
|
+
var osint = new Hono();
|
|
271
|
+
osint.use("*", async (c, next) => {
|
|
272
|
+
if (!env.OSINT_ENABLED) {
|
|
273
|
+
return c.json({ error: "OSINT features are disabled" }, 403);
|
|
274
|
+
}
|
|
275
|
+
await next();
|
|
276
|
+
});
|
|
277
|
+
osint.get("/graph", async (c) => {
|
|
278
|
+
try {
|
|
279
|
+
const graphStart = Date.now();
|
|
280
|
+
const userId = c.req.query("userId") || "system";
|
|
281
|
+
const limit = Math.min(parseInt(c.req.query("limit") || "200", 10), 1e3);
|
|
282
|
+
const since = c.req.query("since");
|
|
283
|
+
const until = c.req.query("until");
|
|
284
|
+
brainTelemetry.emitEvent({
|
|
285
|
+
type: "tool_start",
|
|
286
|
+
timestamp: graphStart,
|
|
287
|
+
requestId: `osint-graph-${graphStart}`,
|
|
288
|
+
data: { toolName: "osint_graph_load", since, until, limit }
|
|
289
|
+
});
|
|
290
|
+
const conditions = [];
|
|
291
|
+
if (since) {
|
|
292
|
+
conditions.push(sql`${graphEntities.createdAt} >= ${new Date(since)}`);
|
|
293
|
+
}
|
|
294
|
+
if (until) {
|
|
295
|
+
conditions.push(sql`${graphEntities.createdAt} <= ${new Date(until)}`);
|
|
296
|
+
}
|
|
297
|
+
const query = db.select({
|
|
298
|
+
id: graphEntities.id,
|
|
299
|
+
name: graphEntities.name,
|
|
300
|
+
type: graphEntities.type,
|
|
301
|
+
importance: graphEntities.importance,
|
|
302
|
+
description: graphEntities.description,
|
|
303
|
+
attributes: graphEntities.attributes,
|
|
304
|
+
aliases: graphEntities.aliases,
|
|
305
|
+
createdAt: graphEntities.createdAt
|
|
306
|
+
}).from(graphEntities);
|
|
307
|
+
const entities = conditions.length > 0 ? await query.where(and3(...conditions)).orderBy(desc(graphEntities.importance)).limit(limit) : await query.orderBy(desc(graphEntities.importance)).limit(limit);
|
|
308
|
+
const entityIds = entities.map((e) => e.id);
|
|
309
|
+
if (entityIds.length === 0) {
|
|
310
|
+
return c.json({ nodes: [], edges: [] });
|
|
311
|
+
}
|
|
312
|
+
const relationships = await db.select({
|
|
313
|
+
id: graphRelationships.id,
|
|
314
|
+
source: graphRelationships.sourceEntityId,
|
|
315
|
+
target: graphRelationships.targetEntityId,
|
|
316
|
+
type: graphRelationships.type,
|
|
317
|
+
strength: graphRelationships.strength
|
|
318
|
+
}).from(graphRelationships).where(
|
|
319
|
+
and3(
|
|
320
|
+
sql`${graphRelationships.sourceEntityId} = ANY(${sql`ARRAY[${sql.join(entityIds.map((id) => sql`${id}`), sql`, `)}]::uuid[]`})`,
|
|
321
|
+
sql`${graphRelationships.targetEntityId} = ANY(${sql`ARRAY[${sql.join(entityIds.map((id) => sql`${id}`), sql`, `)}]::uuid[]`})`
|
|
322
|
+
)
|
|
323
|
+
);
|
|
324
|
+
brainTelemetry.emitEvent({
|
|
325
|
+
type: "tool_complete",
|
|
326
|
+
timestamp: Date.now(),
|
|
327
|
+
requestId: `osint-graph-${graphStart}`,
|
|
328
|
+
data: { toolName: "osint_graph_load", success: true, latencyMs: Date.now() - graphStart, nodeCount: entities.length, edgeCount: relationships.length }
|
|
329
|
+
});
|
|
330
|
+
return c.json({
|
|
331
|
+
nodes: entities,
|
|
332
|
+
edges: relationships,
|
|
333
|
+
stats: {
|
|
334
|
+
totalEntities: entities.length,
|
|
335
|
+
totalRelationships: relationships.length,
|
|
336
|
+
totalSources: new Set(
|
|
337
|
+
entities.flatMap((e) => {
|
|
338
|
+
const srcs = e.attributes?.sources;
|
|
339
|
+
return Array.isArray(srcs) ? srcs.map((s) => s.type || "unknown") : [];
|
|
340
|
+
})
|
|
341
|
+
).size
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
} catch (error) {
|
|
345
|
+
console.error("[OSINT API] /graph error:", error);
|
|
346
|
+
return c.json({ error: "Failed to fetch graph data" }, 500);
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
osint.get("/entity/:id", async (c) => {
|
|
350
|
+
try {
|
|
351
|
+
const id = c.req.param("id");
|
|
352
|
+
const results = await db.select().from(graphEntities).where(eq3(graphEntities.id, id)).limit(1);
|
|
353
|
+
if (results.length === 0) {
|
|
354
|
+
return c.json({ error: "Entity not found" }, 404);
|
|
355
|
+
}
|
|
356
|
+
const entity = results[0];
|
|
357
|
+
const outgoing = await db.select({
|
|
358
|
+
id: graphRelationships.id,
|
|
359
|
+
sourceEntityId: graphRelationships.sourceEntityId,
|
|
360
|
+
targetEntityId: graphRelationships.targetEntityId,
|
|
361
|
+
type: graphRelationships.type,
|
|
362
|
+
strength: graphRelationships.strength,
|
|
363
|
+
context: graphRelationships.context,
|
|
364
|
+
attributes: graphRelationships.attributes,
|
|
365
|
+
targetName: graphEntities.name
|
|
366
|
+
}).from(graphRelationships).innerJoin(graphEntities, eq3(graphRelationships.targetEntityId, graphEntities.id)).where(eq3(graphRelationships.sourceEntityId, id));
|
|
367
|
+
const incoming = await db.select({
|
|
368
|
+
id: graphRelationships.id,
|
|
369
|
+
sourceEntityId: graphRelationships.sourceEntityId,
|
|
370
|
+
targetEntityId: graphRelationships.targetEntityId,
|
|
371
|
+
type: graphRelationships.type,
|
|
372
|
+
strength: graphRelationships.strength,
|
|
373
|
+
context: graphRelationships.context,
|
|
374
|
+
attributes: graphRelationships.attributes,
|
|
375
|
+
sourceName: graphEntities.name
|
|
376
|
+
}).from(graphRelationships).innerJoin(graphEntities, eq3(graphRelationships.sourceEntityId, graphEntities.id)).where(eq3(graphRelationships.targetEntityId, id));
|
|
377
|
+
return c.json({
|
|
378
|
+
entity,
|
|
379
|
+
relationships: {
|
|
380
|
+
outgoing,
|
|
381
|
+
incoming
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
} catch (error) {
|
|
385
|
+
console.error("[OSINT API] /entity/:id error:", error);
|
|
386
|
+
return c.json({ error: "Failed to fetch entity" }, 500);
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
function normalizeFECName(raw) {
|
|
390
|
+
if (!raw.includes(",")) return toTitleCase(raw);
|
|
391
|
+
const [last, ...rest] = raw.split(",").map((s) => s.trim());
|
|
392
|
+
const first = rest.join(" ").trim();
|
|
393
|
+
if (!first) return toTitleCase(last);
|
|
394
|
+
return toTitleCase(`${first} ${last}`);
|
|
395
|
+
}
|
|
396
|
+
function toTitleCase(s) {
|
|
397
|
+
return s.toLowerCase().replace(/\b\w/g, (c) => c.toUpperCase());
|
|
398
|
+
}
|
|
399
|
+
async function searchExternalAPIs(query) {
|
|
400
|
+
const pr = getPublicRecords();
|
|
401
|
+
const newEntityIds = [];
|
|
402
|
+
const [fecCandidates, fecCommittees, ocCompanies] = await Promise.all([
|
|
403
|
+
pr.fec.searchCandidates(query).then((r) => r.slice(0, 10)).catch((err) => {
|
|
404
|
+
console.warn("[OSINT API] FEC candidates search failed:", err.message);
|
|
405
|
+
return [];
|
|
406
|
+
}),
|
|
407
|
+
pr.fec.searchCommittees(query).then((r) => r.slice(0, 10)).catch((err) => {
|
|
408
|
+
console.warn("[OSINT API] FEC committees search failed:", err.message);
|
|
409
|
+
return [];
|
|
410
|
+
}),
|
|
411
|
+
pr.opencorporates.searchCompanies(query, "us").then((r) => r.slice(0, 10)).catch((err) => {
|
|
412
|
+
console.warn("[OSINT API] OpenCorporates search failed:", err.message);
|
|
413
|
+
return [];
|
|
414
|
+
})
|
|
415
|
+
]);
|
|
416
|
+
const candidates = [];
|
|
417
|
+
for (const c of fecCandidates) {
|
|
418
|
+
candidates.push({
|
|
419
|
+
name: normalizeFECName(c.name),
|
|
420
|
+
type: "person",
|
|
421
|
+
source: "fec",
|
|
422
|
+
identifiers: { fecId: c.candidateId },
|
|
423
|
+
attributes: {
|
|
424
|
+
party: c.party,
|
|
425
|
+
office: c.office,
|
|
426
|
+
state: c.state,
|
|
427
|
+
district: c.district,
|
|
428
|
+
cycles: c.cycles
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
for (const c of fecCommittees) {
|
|
433
|
+
candidates.push({
|
|
434
|
+
name: toTitleCase(c.name),
|
|
435
|
+
type: "committee",
|
|
436
|
+
source: "fec",
|
|
437
|
+
identifiers: { fecId: c.committeeId },
|
|
438
|
+
attributes: {
|
|
439
|
+
designation: c.designation,
|
|
440
|
+
committeeType: c.type,
|
|
441
|
+
party: c.party,
|
|
442
|
+
state: c.state,
|
|
443
|
+
treasurerName: c.treasurerName
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
for (const c of ocCompanies) {
|
|
448
|
+
candidates.push({
|
|
449
|
+
name: c.name,
|
|
450
|
+
type: "organization",
|
|
451
|
+
source: "opencorporates",
|
|
452
|
+
attributes: {
|
|
453
|
+
companyNumber: c.companyNumber,
|
|
454
|
+
jurisdiction: c.jurisdictionCode,
|
|
455
|
+
status: c.status,
|
|
456
|
+
companyType: c.companyType,
|
|
457
|
+
incorporationDate: c.incorporationDate,
|
|
458
|
+
registeredAddress: c.registeredAddress,
|
|
459
|
+
openCorporatesUrl: c.openCorporatesUrl
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
for (const candidate of candidates) {
|
|
464
|
+
try {
|
|
465
|
+
const resolved = await resolveEntity(candidate);
|
|
466
|
+
newEntityIds.push(resolved.entityId);
|
|
467
|
+
} catch (err) {
|
|
468
|
+
console.warn(`[OSINT API] Entity resolution failed for "${candidate.name}":`, err.message);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
return [...new Set(newEntityIds)];
|
|
472
|
+
}
|
|
473
|
+
osint.get("/search", async (c) => {
|
|
474
|
+
try {
|
|
475
|
+
const q = c.req.query("q");
|
|
476
|
+
const type = c.req.query("type");
|
|
477
|
+
const limit = Math.min(parseInt(c.req.query("limit") || "25", 10), 100);
|
|
478
|
+
if (!q) {
|
|
479
|
+
return c.json({ error: "Query parameter 'q' is required" }, 400);
|
|
480
|
+
}
|
|
481
|
+
const searchStart = Date.now();
|
|
482
|
+
brainTelemetry.emitEvent({
|
|
483
|
+
type: "tool_start",
|
|
484
|
+
timestamp: searchStart,
|
|
485
|
+
requestId: `osint-search-${searchStart}`,
|
|
486
|
+
data: { toolName: "osint_search", query: q, entityType: type }
|
|
487
|
+
});
|
|
488
|
+
let query = db.select().from(graphEntities).where(
|
|
489
|
+
type ? and3(ilike(graphEntities.name, `%${q}%`), eq3(graphEntities.type, type)) : ilike(graphEntities.name, `%${q}%`)
|
|
490
|
+
).orderBy(desc(graphEntities.importance)).limit(limit);
|
|
491
|
+
let results = await query;
|
|
492
|
+
let externalSearched = false;
|
|
493
|
+
if (results.length < 3 && q.length >= 2) {
|
|
494
|
+
try {
|
|
495
|
+
console.log(`[OSINT API] Local results (${results.length}) < 3 for "${q}", querying external APIs...`);
|
|
496
|
+
const externalIds = await searchExternalAPIs(q);
|
|
497
|
+
externalSearched = true;
|
|
498
|
+
if (externalIds.length > 0) {
|
|
499
|
+
const nameCondition = type ? and3(ilike(graphEntities.name, `%${q}%`), eq3(graphEntities.type, type)) : ilike(graphEntities.name, `%${q}%`);
|
|
500
|
+
const idCondition = sql`${graphEntities.id} = ANY(${sql`ARRAY[${sql.join(externalIds.map((id) => sql`${id}`), sql`, `)}]::uuid[]`})`;
|
|
501
|
+
results = await db.select().from(graphEntities).where(sql`(${nameCondition}) OR (${idCondition})`).orderBy(desc(graphEntities.importance)).limit(limit);
|
|
502
|
+
}
|
|
503
|
+
} catch (extErr) {
|
|
504
|
+
console.warn("[OSINT API] External search failed (graceful):", extErr.message);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
const entityIds = results.map((e) => e.id);
|
|
508
|
+
let edges = [];
|
|
509
|
+
if (entityIds.length > 1) {
|
|
510
|
+
edges = await db.select({
|
|
511
|
+
id: graphRelationships.id,
|
|
512
|
+
source: graphRelationships.sourceEntityId,
|
|
513
|
+
target: graphRelationships.targetEntityId,
|
|
514
|
+
type: graphRelationships.type,
|
|
515
|
+
strength: graphRelationships.strength
|
|
516
|
+
}).from(graphRelationships).where(
|
|
517
|
+
and3(
|
|
518
|
+
sql`${graphRelationships.sourceEntityId} = ANY(${sql`ARRAY[${sql.join(entityIds.map((id) => sql`${id}`), sql`, `)}]::uuid[]`})`,
|
|
519
|
+
sql`${graphRelationships.targetEntityId} = ANY(${sql`ARRAY[${sql.join(entityIds.map((id) => sql`${id}`), sql`, `)}]::uuid[]`})`
|
|
520
|
+
)
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
const searchLatency = Date.now() - searchStart;
|
|
524
|
+
brainTelemetry.emitEvent({
|
|
525
|
+
type: "tool_complete",
|
|
526
|
+
timestamp: Date.now(),
|
|
527
|
+
requestId: `osint-search-${searchStart}`,
|
|
528
|
+
data: { toolName: "osint_search", success: true, latencyMs: searchLatency, resultCount: results.length, externalSearched }
|
|
529
|
+
});
|
|
530
|
+
audit.toolUse("web:dashboard", "osint_search", { query: q, entityType: type, resultCount: results.length, externalSearched, latencyMs: searchLatency }, true).catch(() => {
|
|
531
|
+
});
|
|
532
|
+
return c.json({ results, edges, total: results.length, externalSearched });
|
|
533
|
+
} catch (error) {
|
|
534
|
+
console.error("[OSINT API] /search error:", error);
|
|
535
|
+
brainTelemetry.emitEvent({
|
|
536
|
+
type: "tool_complete",
|
|
537
|
+
timestamp: Date.now(),
|
|
538
|
+
requestId: `osint-search-${Date.now()}`,
|
|
539
|
+
data: { toolName: "osint_search", success: false }
|
|
540
|
+
});
|
|
541
|
+
audit.toolUse("web:dashboard", "osint_search", { query: c.req.query("q"), error: error?.message }, false).catch(() => {
|
|
542
|
+
});
|
|
543
|
+
return c.json({ error: "Search failed" }, 500);
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
osint.post("/enrich", async (c) => {
|
|
547
|
+
try {
|
|
548
|
+
const body = await c.req.json();
|
|
549
|
+
if (!body.entityId) {
|
|
550
|
+
return c.json({ error: "entityId is required" }, 400);
|
|
551
|
+
}
|
|
552
|
+
const entity = await db.select().from(graphEntities).where(eq3(graphEntities.id, body.entityId)).limit(1);
|
|
553
|
+
if (entity.length === 0) {
|
|
554
|
+
return c.json({ error: "Entity not found" }, 404);
|
|
555
|
+
}
|
|
556
|
+
const enrichStart = Date.now();
|
|
557
|
+
const entityName = entity[0].name || body.entityId;
|
|
558
|
+
brainTelemetry.emitEvent({
|
|
559
|
+
type: "tool_start",
|
|
560
|
+
timestamp: enrichStart,
|
|
561
|
+
requestId: `osint-enrich-${enrichStart}`,
|
|
562
|
+
data: { toolName: "osint_enrich", entityId: body.entityId, entityName, sources: body.sources, depth: body.depth ?? 1 }
|
|
563
|
+
});
|
|
564
|
+
const { enrichEntity } = await import("./enrichment-pipeline-CMUVBDC7.js");
|
|
565
|
+
const result = await enrichEntity(body.entityId, body.sources, body.depth ?? 1);
|
|
566
|
+
const enrichLatency = Date.now() - enrichStart;
|
|
567
|
+
brainTelemetry.emitEvent({
|
|
568
|
+
type: "tool_complete",
|
|
569
|
+
timestamp: Date.now(),
|
|
570
|
+
requestId: `osint-enrich-${enrichStart}`,
|
|
571
|
+
data: { toolName: "osint_enrich", success: true, latencyMs: enrichLatency, entityName, newEntities: result?.newEntitiesCreated, newRelationships: result?.newRelationshipsCreated }
|
|
572
|
+
});
|
|
573
|
+
audit.toolUse("web:dashboard", "osint_enrich", { entityId: body.entityId, entityName, sources: body.sources, depth: body.depth, newEntities: result?.newEntitiesCreated, newRelationships: result?.newRelationshipsCreated, latencyMs: enrichLatency }, true).catch(() => {
|
|
574
|
+
});
|
|
575
|
+
return c.json({ success: true, result });
|
|
576
|
+
} catch (error) {
|
|
577
|
+
if (error?.code === "MODULE_NOT_FOUND" || error?.message?.includes("Cannot find module")) {
|
|
578
|
+
return c.json({ error: "Enrichment pipeline is not available" }, 501);
|
|
579
|
+
}
|
|
580
|
+
console.error("[OSINT API] /enrich error:", error);
|
|
581
|
+
brainTelemetry.emitEvent({
|
|
582
|
+
type: "tool_complete",
|
|
583
|
+
timestamp: Date.now(),
|
|
584
|
+
requestId: `osint-enrich-${Date.now()}`,
|
|
585
|
+
data: { toolName: "osint_enrich", success: false, error: error?.message }
|
|
586
|
+
});
|
|
587
|
+
audit.toolUse("web:dashboard", "osint_enrich", { error: error?.message }, false).catch(() => {
|
|
588
|
+
});
|
|
589
|
+
return c.json({ error: "Enrichment failed" }, 500);
|
|
590
|
+
}
|
|
591
|
+
});
|
|
592
|
+
var FINANCIAL_REL_TYPES = ["donated_to", "funded", "awarded_contract", "grant_to", "paid"];
|
|
593
|
+
osint.get("/financial-flow", async (c) => {
|
|
594
|
+
try {
|
|
595
|
+
const entityId = c.req.query("entityId");
|
|
596
|
+
if (!entityId) {
|
|
597
|
+
return c.json({ error: "Query parameter 'entityId' is required" }, 400);
|
|
598
|
+
}
|
|
599
|
+
const rootEntity = await db.select({ id: graphEntities.id, name: graphEntities.name }).from(graphEntities).where(eq3(graphEntities.id, entityId)).limit(1);
|
|
600
|
+
if (rootEntity.length === 0) {
|
|
601
|
+
return c.json({ error: "Entity not found" }, 404);
|
|
602
|
+
}
|
|
603
|
+
const outgoing = await db.select({
|
|
604
|
+
id: graphRelationships.id,
|
|
605
|
+
sourceEntityId: graphRelationships.sourceEntityId,
|
|
606
|
+
targetEntityId: graphRelationships.targetEntityId,
|
|
607
|
+
type: graphRelationships.type,
|
|
608
|
+
strength: graphRelationships.strength,
|
|
609
|
+
attributes: graphRelationships.attributes
|
|
610
|
+
}).from(graphRelationships).where(
|
|
611
|
+
and3(
|
|
612
|
+
eq3(graphRelationships.sourceEntityId, entityId),
|
|
613
|
+
sql`${graphRelationships.type} = ANY(${sql`ARRAY[${sql.join(FINANCIAL_REL_TYPES.map((t) => sql`${t}`), sql`, `)}]::text[]`})`
|
|
614
|
+
)
|
|
615
|
+
);
|
|
616
|
+
const incoming = await db.select({
|
|
617
|
+
id: graphRelationships.id,
|
|
618
|
+
sourceEntityId: graphRelationships.sourceEntityId,
|
|
619
|
+
targetEntityId: graphRelationships.targetEntityId,
|
|
620
|
+
type: graphRelationships.type,
|
|
621
|
+
strength: graphRelationships.strength,
|
|
622
|
+
attributes: graphRelationships.attributes
|
|
623
|
+
}).from(graphRelationships).where(
|
|
624
|
+
and3(
|
|
625
|
+
eq3(graphRelationships.targetEntityId, entityId),
|
|
626
|
+
sql`${graphRelationships.type} = ANY(${sql`ARRAY[${sql.join(FINANCIAL_REL_TYPES.map((t) => sql`${t}`), sql`, `)}]::text[]`})`
|
|
627
|
+
)
|
|
628
|
+
);
|
|
629
|
+
const allRels = [...outgoing, ...incoming];
|
|
630
|
+
const relatedIds = /* @__PURE__ */ new Set();
|
|
631
|
+
relatedIds.add(entityId);
|
|
632
|
+
for (const rel of allRels) {
|
|
633
|
+
relatedIds.add(rel.sourceEntityId);
|
|
634
|
+
relatedIds.add(rel.targetEntityId);
|
|
635
|
+
}
|
|
636
|
+
const relatedEntities = await db.select({
|
|
637
|
+
id: graphEntities.id,
|
|
638
|
+
name: graphEntities.name,
|
|
639
|
+
type: graphEntities.type
|
|
640
|
+
}).from(graphEntities).where(sql`${graphEntities.id} = ANY(${sql`ARRAY[${sql.join([...relatedIds].map((id) => sql`${id}`), sql`, `)}]::uuid[]`})`);
|
|
641
|
+
const entityMap = new Map(relatedEntities.map((e) => [e.id, e]));
|
|
642
|
+
const nodeValues = /* @__PURE__ */ new Map();
|
|
643
|
+
for (const rel of allRels) {
|
|
644
|
+
const attrs = rel.attributes || {};
|
|
645
|
+
const amount = attrs.amount || rel.strength || 1;
|
|
646
|
+
nodeValues.set(rel.sourceEntityId, (nodeValues.get(rel.sourceEntityId) || 0) + amount);
|
|
647
|
+
nodeValues.set(rel.targetEntityId, (nodeValues.get(rel.targetEntityId) || 0) + amount);
|
|
648
|
+
}
|
|
649
|
+
const nodes = relatedEntities.map((e) => ({
|
|
650
|
+
id: e.id,
|
|
651
|
+
name: e.name,
|
|
652
|
+
type: e.type,
|
|
653
|
+
value: nodeValues.get(e.id) || 0
|
|
654
|
+
}));
|
|
655
|
+
const links = allRels.filter((rel) => entityMap.has(rel.sourceEntityId) && entityMap.has(rel.targetEntityId)).map((rel) => {
|
|
656
|
+
const attrs = rel.attributes || {};
|
|
657
|
+
return {
|
|
658
|
+
source: rel.sourceEntityId,
|
|
659
|
+
target: rel.targetEntityId,
|
|
660
|
+
value: attrs.amount || rel.strength || 1,
|
|
661
|
+
description: `${rel.type.replace(/_/g, " ")}${attrs.period ? ` (${attrs.period})` : ""}`
|
|
662
|
+
};
|
|
663
|
+
});
|
|
664
|
+
return c.json({ nodes, links });
|
|
665
|
+
} catch (error) {
|
|
666
|
+
console.error("[OSINT API] /financial-flow error:", error);
|
|
667
|
+
return c.json({ error: "Failed to fetch financial flow data" }, 500);
|
|
668
|
+
}
|
|
669
|
+
});
|
|
670
|
+
osint.get("/duplicates", async (c) => {
|
|
671
|
+
try {
|
|
672
|
+
const threshold = parseFloat(c.req.query("threshold") || "0.85");
|
|
673
|
+
const { findDuplicates } = await import("./entity-resolution-4X4JU43O.js");
|
|
674
|
+
const duplicates = await findDuplicates(threshold);
|
|
675
|
+
return c.json({ duplicates, total: duplicates.length });
|
|
676
|
+
} catch (error) {
|
|
677
|
+
console.error("[OSINT API] /duplicates error:", error);
|
|
678
|
+
return c.json({ error: "Failed to find duplicates" }, 500);
|
|
679
|
+
}
|
|
680
|
+
});
|
|
681
|
+
osint.get("/stats", async (c) => {
|
|
682
|
+
try {
|
|
683
|
+
const totalEntitiesResult = await db.select({ count: sql`count(*)::int` }).from(graphEntities);
|
|
684
|
+
const totalEntities = totalEntitiesResult[0]?.count ?? 0;
|
|
685
|
+
const totalRelsResult = await db.select({ count: sql`count(*)::int` }).from(graphRelationships);
|
|
686
|
+
const totalRelationships = totalRelsResult[0]?.count ?? 0;
|
|
687
|
+
const entitiesByType = await db.select({
|
|
688
|
+
type: graphEntities.type,
|
|
689
|
+
count: sql`count(*)::int`
|
|
690
|
+
}).from(graphEntities).groupBy(graphEntities.type).orderBy(desc(sql`count(*)`));
|
|
691
|
+
const topEntities = await db.select({
|
|
692
|
+
id: graphEntities.id,
|
|
693
|
+
name: graphEntities.name,
|
|
694
|
+
type: graphEntities.type,
|
|
695
|
+
mentionCount: graphEntities.mentionCount,
|
|
696
|
+
importance: graphEntities.importance
|
|
697
|
+
}).from(graphEntities).orderBy(desc(graphEntities.mentionCount)).limit(20);
|
|
698
|
+
return c.json({
|
|
699
|
+
totalEntities,
|
|
700
|
+
totalRelationships,
|
|
701
|
+
entitiesByType,
|
|
702
|
+
topEntities
|
|
703
|
+
});
|
|
704
|
+
} catch (error) {
|
|
705
|
+
console.error("[OSINT API] /stats error:", error);
|
|
706
|
+
return c.json({ error: "Failed to fetch statistics" }, 500);
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
// src/inputs/api/routes/email.ts
|
|
711
|
+
import { Hono as Hono2 } from "hono";
|
|
712
|
+
init_imap_client();
|
|
713
|
+
init_smtp_client();
|
|
714
|
+
var email = new Hono2();
|
|
715
|
+
async function connectImapClientWithFallback(emailAddress) {
|
|
716
|
+
if (env.EMAIL_MASTER_USER && env.EMAIL_MASTER_PASSWORD) {
|
|
717
|
+
const authUser = `${emailAddress}*${env.EMAIL_MASTER_USER}`;
|
|
718
|
+
const localClient = new ImapClient({
|
|
719
|
+
host: env.EMAIL_LOCAL_IMAP_HOST || "127.0.0.1",
|
|
720
|
+
port: env.EMAIL_LOCAL_IMAP_PORT || 993,
|
|
721
|
+
secure: true,
|
|
722
|
+
user: authUser,
|
|
723
|
+
password: env.EMAIL_MASTER_PASSWORD,
|
|
724
|
+
tls: { rejectUnauthorized: false }
|
|
725
|
+
});
|
|
726
|
+
try {
|
|
727
|
+
await localClient.connect();
|
|
728
|
+
return localClient;
|
|
729
|
+
} catch (localErr) {
|
|
730
|
+
const remoteHost = env.EMAIL_IMAP_HOST;
|
|
731
|
+
const remotePort = env.EMAIL_IMAP_PORT || 993;
|
|
732
|
+
if (!remoteHost) throw localErr;
|
|
733
|
+
console.log(
|
|
734
|
+
`[Email API] Local IMAP unavailable (${localErr.code || localErr.message}), falling back to ${remoteHost}:${remotePort}`
|
|
735
|
+
);
|
|
736
|
+
const remoteClient = new ImapClient({
|
|
737
|
+
host: remoteHost,
|
|
738
|
+
port: remotePort,
|
|
739
|
+
secure: true,
|
|
740
|
+
user: authUser,
|
|
741
|
+
password: env.EMAIL_MASTER_PASSWORD,
|
|
742
|
+
tls: { rejectUnauthorized: false }
|
|
743
|
+
});
|
|
744
|
+
await remoteClient.connect();
|
|
745
|
+
return remoteClient;
|
|
746
|
+
}
|
|
747
|
+
} else if (env.EMAIL_USER && env.EMAIL_PASSWORD) {
|
|
748
|
+
const client = new ImapClient({
|
|
749
|
+
host: env.EMAIL_IMAP_HOST || "imap.gmail.com",
|
|
750
|
+
port: env.EMAIL_IMAP_PORT || 993,
|
|
751
|
+
secure: env.EMAIL_IMAP_SECURE !== false,
|
|
752
|
+
user: env.EMAIL_USER,
|
|
753
|
+
password: env.EMAIL_PASSWORD
|
|
754
|
+
});
|
|
755
|
+
await client.connect();
|
|
756
|
+
return client;
|
|
757
|
+
}
|
|
758
|
+
throw new Error("Email not configured");
|
|
759
|
+
}
|
|
760
|
+
function createSmtpClient(fromAddress) {
|
|
761
|
+
if (env.EMAIL_MASTER_USER) {
|
|
762
|
+
return new SmtpClient(
|
|
763
|
+
{
|
|
764
|
+
host: env.EMAIL_LOCAL_SMTP_HOST || "127.0.0.1",
|
|
765
|
+
port: env.EMAIL_LOCAL_SMTP_PORT || 25,
|
|
766
|
+
secure: false,
|
|
767
|
+
auth: { user: "", pass: "" },
|
|
768
|
+
tls: { rejectUnauthorized: false }
|
|
769
|
+
},
|
|
770
|
+
fromAddress
|
|
771
|
+
);
|
|
772
|
+
} else if (env.EMAIL_USER && env.EMAIL_PASSWORD) {
|
|
773
|
+
return new SmtpClient(
|
|
774
|
+
{
|
|
775
|
+
host: env.EMAIL_SMTP_HOST || "smtp.gmail.com",
|
|
776
|
+
port: env.EMAIL_SMTP_PORT || 587,
|
|
777
|
+
secure: env.EMAIL_SMTP_SECURE === true,
|
|
778
|
+
auth: { user: env.EMAIL_USER, pass: env.EMAIL_PASSWORD }
|
|
779
|
+
},
|
|
780
|
+
fromAddress
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
throw new Error("Email not configured");
|
|
784
|
+
}
|
|
785
|
+
function serializeEmail(email2, includeBody) {
|
|
786
|
+
const serialized = {
|
|
787
|
+
id: email2.id,
|
|
788
|
+
uid: email2.uid,
|
|
789
|
+
messageId: email2.messageId,
|
|
790
|
+
subject: email2.subject,
|
|
791
|
+
from: email2.from,
|
|
792
|
+
to: email2.to,
|
|
793
|
+
cc: email2.cc,
|
|
794
|
+
bcc: email2.bcc,
|
|
795
|
+
date: email2.date.toISOString(),
|
|
796
|
+
text: includeBody ? email2.text : email2.snippet,
|
|
797
|
+
html: includeBody ? email2.html : "",
|
|
798
|
+
snippet: email2.snippet,
|
|
799
|
+
attachments: email2.attachments.map((a) => ({
|
|
800
|
+
filename: a.filename,
|
|
801
|
+
contentType: a.contentType,
|
|
802
|
+
size: a.size,
|
|
803
|
+
contentId: a.contentId,
|
|
804
|
+
hasContent: !!a.content
|
|
805
|
+
})),
|
|
806
|
+
flags: email2.flags,
|
|
807
|
+
labels: email2.labels,
|
|
808
|
+
threadId: email2.threadId,
|
|
809
|
+
inReplyTo: email2.inReplyTo,
|
|
810
|
+
references: email2.references,
|
|
811
|
+
headers: Object.fromEntries(email2.headers)
|
|
812
|
+
};
|
|
813
|
+
return serialized;
|
|
814
|
+
}
|
|
815
|
+
email.use("*", async (c, next) => {
|
|
816
|
+
if (!env.EMAIL_MASTER_USER && !env.EMAIL_USER) {
|
|
817
|
+
return c.json(
|
|
818
|
+
{ error: "Email not configured. Set EMAIL_MASTER_USER or EMAIL_USER in .env" },
|
|
819
|
+
503
|
|
820
|
+
);
|
|
821
|
+
}
|
|
822
|
+
await next();
|
|
823
|
+
});
|
|
824
|
+
email.get("/folders", async (c) => {
|
|
825
|
+
const emailAddress = c.req.query("email_address");
|
|
826
|
+
if (!emailAddress) {
|
|
827
|
+
return c.json({ error: "Query parameter 'email_address' is required" }, 400);
|
|
828
|
+
}
|
|
829
|
+
let imap = null;
|
|
830
|
+
try {
|
|
831
|
+
imap = await connectImapClientWithFallback(emailAddress);
|
|
832
|
+
const folders = await imap.listFolders();
|
|
833
|
+
return c.json(folders);
|
|
834
|
+
} catch (error) {
|
|
835
|
+
console.error("[Email API] /folders error:", error);
|
|
836
|
+
return c.json(
|
|
837
|
+
{ error: error instanceof Error ? error.message : "Failed to list folders" },
|
|
838
|
+
500
|
|
839
|
+
);
|
|
840
|
+
} finally {
|
|
841
|
+
if (imap) {
|
|
842
|
+
await imap.disconnect().catch(() => {
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
});
|
|
847
|
+
email.get("/inbox", async (c) => {
|
|
848
|
+
const emailAddress = c.req.query("email_address");
|
|
849
|
+
if (!emailAddress) {
|
|
850
|
+
return c.json({ error: "Query parameter 'email_address' is required" }, 400);
|
|
851
|
+
}
|
|
852
|
+
const folder = c.req.query("folder") || "INBOX";
|
|
853
|
+
const limit = c.req.query("limit") || "20";
|
|
854
|
+
const offset = c.req.query("offset") || "0";
|
|
855
|
+
const unreadOnly = c.req.query("unread_only") === "true";
|
|
856
|
+
let imap = null;
|
|
857
|
+
try {
|
|
858
|
+
imap = await connectImapClientWithFallback(emailAddress);
|
|
859
|
+
let emails;
|
|
860
|
+
if (unreadOnly) {
|
|
861
|
+
emails = await imap.searchEmails({
|
|
862
|
+
folder,
|
|
863
|
+
seen: false,
|
|
864
|
+
limit: parseInt(limit)
|
|
865
|
+
});
|
|
866
|
+
} else {
|
|
867
|
+
emails = await imap.fetchEmails(folder, {
|
|
868
|
+
limit: parseInt(limit),
|
|
869
|
+
offset: parseInt(offset)
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
return c.json({
|
|
873
|
+
emails: emails.map((e) => serializeEmail(e, false)),
|
|
874
|
+
folder
|
|
875
|
+
});
|
|
876
|
+
} catch (error) {
|
|
877
|
+
console.error("[Email API] /inbox error:", error);
|
|
878
|
+
return c.json(
|
|
879
|
+
{ error: error instanceof Error ? error.message : "Failed to fetch emails" },
|
|
880
|
+
500
|
|
881
|
+
);
|
|
882
|
+
} finally {
|
|
883
|
+
if (imap) {
|
|
884
|
+
await imap.disconnect().catch(() => {
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
});
|
|
889
|
+
email.get("/message/:uid", async (c) => {
|
|
890
|
+
const emailAddress = c.req.query("email_address");
|
|
891
|
+
if (!emailAddress) {
|
|
892
|
+
return c.json({ error: "Query parameter 'email_address' is required" }, 400);
|
|
893
|
+
}
|
|
894
|
+
const uid = parseInt(c.req.param("uid"));
|
|
895
|
+
const folder = c.req.query("folder") || "INBOX";
|
|
896
|
+
let imap = null;
|
|
897
|
+
try {
|
|
898
|
+
imap = await connectImapClientWithFallback(emailAddress);
|
|
899
|
+
const emailMsg = await imap.fetchEmail(uid, folder);
|
|
900
|
+
if (!emailMsg) {
|
|
901
|
+
return c.json({ error: "Email not found" }, 404);
|
|
902
|
+
}
|
|
903
|
+
await imap.markAsRead(uid, folder);
|
|
904
|
+
return c.json(serializeEmail(emailMsg, true));
|
|
905
|
+
} catch (error) {
|
|
906
|
+
console.error("[Email API] /message/:uid error:", error);
|
|
907
|
+
return c.json(
|
|
908
|
+
{ error: error instanceof Error ? error.message : "Failed to fetch email" },
|
|
909
|
+
500
|
|
910
|
+
);
|
|
911
|
+
} finally {
|
|
912
|
+
if (imap) {
|
|
913
|
+
await imap.disconnect().catch(() => {
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
});
|
|
918
|
+
email.get("/attachment/:uid/:index", async (c) => {
|
|
919
|
+
const emailAddress = c.req.query("email_address");
|
|
920
|
+
if (!emailAddress) {
|
|
921
|
+
return c.json({ error: "Query parameter 'email_address' is required" }, 400);
|
|
922
|
+
}
|
|
923
|
+
const uid = parseInt(c.req.param("uid"));
|
|
924
|
+
const index = parseInt(c.req.param("index"));
|
|
925
|
+
const folder = c.req.query("folder") || "INBOX";
|
|
926
|
+
let imap = null;
|
|
927
|
+
try {
|
|
928
|
+
imap = await connectImapClientWithFallback(emailAddress);
|
|
929
|
+
const emailMsg = await imap.fetchEmail(uid, folder);
|
|
930
|
+
if (!emailMsg) {
|
|
931
|
+
return c.json({ error: "Email not found" }, 404);
|
|
932
|
+
}
|
|
933
|
+
const attachment = emailMsg.attachments[index];
|
|
934
|
+
if (!attachment) {
|
|
935
|
+
return c.json({ error: "Attachment not found" }, 404);
|
|
936
|
+
}
|
|
937
|
+
if (!attachment.content) {
|
|
938
|
+
return c.json({ error: "Attachment content not available" }, 404);
|
|
939
|
+
}
|
|
940
|
+
return new Response(new Uint8Array(attachment.content), {
|
|
941
|
+
headers: {
|
|
942
|
+
"Content-Type": attachment.contentType || "application/octet-stream",
|
|
943
|
+
"Content-Disposition": `attachment; filename="${attachment.filename}"`,
|
|
944
|
+
"Content-Length": String(attachment.size || attachment.content.length)
|
|
945
|
+
}
|
|
946
|
+
});
|
|
947
|
+
} catch (error) {
|
|
948
|
+
console.error("[Email API] /attachment/:uid/:index error:", error);
|
|
949
|
+
return c.json(
|
|
950
|
+
{ error: error instanceof Error ? error.message : "Failed to fetch attachment" },
|
|
951
|
+
500
|
|
952
|
+
);
|
|
953
|
+
} finally {
|
|
954
|
+
if (imap) {
|
|
955
|
+
await imap.disconnect().catch(() => {
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
});
|
|
960
|
+
email.post("/send", async (c) => {
|
|
961
|
+
try {
|
|
962
|
+
const body = await c.req.json();
|
|
963
|
+
if (!body.from || !body.to || !body.subject) {
|
|
964
|
+
return c.json({ error: "Fields 'from', 'to', and 'subject' are required" }, 400);
|
|
965
|
+
}
|
|
966
|
+
const smtp = createSmtpClient(body.from);
|
|
967
|
+
const convertedAttachments = body.attachments?.map((a) => ({
|
|
968
|
+
filename: a.filename,
|
|
969
|
+
contentType: a.contentType,
|
|
970
|
+
size: Buffer.from(a.content, "base64").length,
|
|
971
|
+
content: Buffer.from(a.content, "base64")
|
|
972
|
+
}));
|
|
973
|
+
const result = await smtp.send({
|
|
974
|
+
to: body.to,
|
|
975
|
+
cc: body.cc,
|
|
976
|
+
bcc: body.bcc,
|
|
977
|
+
subject: body.subject,
|
|
978
|
+
text: body.text,
|
|
979
|
+
html: body.html,
|
|
980
|
+
attachments: convertedAttachments
|
|
981
|
+
});
|
|
982
|
+
return c.json(result);
|
|
983
|
+
} catch (error) {
|
|
984
|
+
console.error("[Email API] /send error:", error);
|
|
985
|
+
return c.json(
|
|
986
|
+
{ error: error instanceof Error ? error.message : "Failed to send email" },
|
|
987
|
+
500
|
|
988
|
+
);
|
|
989
|
+
}
|
|
990
|
+
});
|
|
991
|
+
email.post("/reply", async (c) => {
|
|
992
|
+
const body = await c.req.json();
|
|
993
|
+
if (!body.email_address || !body.email_uid || !body.body) {
|
|
994
|
+
return c.json(
|
|
995
|
+
{ error: "Fields 'email_address', 'email_uid', and 'body' are required" },
|
|
996
|
+
400
|
|
997
|
+
);
|
|
998
|
+
}
|
|
999
|
+
const folder = body.folder || "INBOX";
|
|
1000
|
+
let imap = null;
|
|
1001
|
+
try {
|
|
1002
|
+
imap = await connectImapClientWithFallback(body.email_address);
|
|
1003
|
+
const originalEmail = await imap.fetchEmail(body.email_uid, folder);
|
|
1004
|
+
if (!originalEmail) {
|
|
1005
|
+
return c.json({ error: "Original email not found" }, 404);
|
|
1006
|
+
}
|
|
1007
|
+
const smtp = createSmtpClient(body.email_address);
|
|
1008
|
+
const convertedAttachments = body.attachments?.map((a) => ({
|
|
1009
|
+
filename: a.filename,
|
|
1010
|
+
contentType: a.contentType,
|
|
1011
|
+
size: Buffer.from(a.content, "base64").length,
|
|
1012
|
+
content: Buffer.from(a.content, "base64")
|
|
1013
|
+
}));
|
|
1014
|
+
let result;
|
|
1015
|
+
if (body.reply_all) {
|
|
1016
|
+
result = await smtp.replyAll(
|
|
1017
|
+
originalEmail,
|
|
1018
|
+
{ text: body.body, html: body.html, attachments: convertedAttachments },
|
|
1019
|
+
body.email_address
|
|
1020
|
+
);
|
|
1021
|
+
} else {
|
|
1022
|
+
result = await smtp.reply(originalEmail, {
|
|
1023
|
+
text: body.body,
|
|
1024
|
+
html: body.html,
|
|
1025
|
+
attachments: convertedAttachments
|
|
1026
|
+
});
|
|
1027
|
+
}
|
|
1028
|
+
return c.json(result);
|
|
1029
|
+
} catch (error) {
|
|
1030
|
+
console.error("[Email API] /reply error:", error);
|
|
1031
|
+
return c.json(
|
|
1032
|
+
{ error: error instanceof Error ? error.message : "Failed to send reply" },
|
|
1033
|
+
500
|
|
1034
|
+
);
|
|
1035
|
+
} finally {
|
|
1036
|
+
if (imap) {
|
|
1037
|
+
await imap.disconnect().catch(() => {
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
});
|
|
1042
|
+
email.post("/search", async (c) => {
|
|
1043
|
+
const body = await c.req.json();
|
|
1044
|
+
if (!body.email_address) {
|
|
1045
|
+
return c.json({ error: "Field 'email_address' is required" }, 400);
|
|
1046
|
+
}
|
|
1047
|
+
let imap = null;
|
|
1048
|
+
try {
|
|
1049
|
+
imap = await connectImapClientWithFallback(body.email_address);
|
|
1050
|
+
const searchOptions = {
|
|
1051
|
+
folder: body.folder || "INBOX",
|
|
1052
|
+
from: body.from,
|
|
1053
|
+
to: body.to,
|
|
1054
|
+
subject: body.subject,
|
|
1055
|
+
body: body.body,
|
|
1056
|
+
since: body.since ? new Date(body.since) : void 0,
|
|
1057
|
+
before: body.before ? new Date(body.before) : void 0,
|
|
1058
|
+
seen: body.unread_only ? false : void 0,
|
|
1059
|
+
limit: body.limit
|
|
1060
|
+
};
|
|
1061
|
+
const emails = await imap.searchEmails(searchOptions);
|
|
1062
|
+
return c.json({
|
|
1063
|
+
emails: emails.map((e) => serializeEmail(e, false))
|
|
1064
|
+
});
|
|
1065
|
+
} catch (error) {
|
|
1066
|
+
console.error("[Email API] /search error:", error);
|
|
1067
|
+
return c.json(
|
|
1068
|
+
{ error: error instanceof Error ? error.message : "Search failed" },
|
|
1069
|
+
500
|
|
1070
|
+
);
|
|
1071
|
+
} finally {
|
|
1072
|
+
if (imap) {
|
|
1073
|
+
await imap.disconnect().catch(() => {
|
|
1074
|
+
});
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
});
|
|
1078
|
+
email.post("/flag", async (c) => {
|
|
1079
|
+
const body = await c.req.json();
|
|
1080
|
+
if (!body.email_address || !body.uid || !body.action) {
|
|
1081
|
+
return c.json(
|
|
1082
|
+
{ error: "Fields 'email_address', 'uid', and 'action' are required" },
|
|
1083
|
+
400
|
|
1084
|
+
);
|
|
1085
|
+
}
|
|
1086
|
+
const folder = body.folder || "INBOX";
|
|
1087
|
+
let imap = null;
|
|
1088
|
+
try {
|
|
1089
|
+
imap = await connectImapClientWithFallback(body.email_address);
|
|
1090
|
+
switch (body.action) {
|
|
1091
|
+
case "read":
|
|
1092
|
+
await imap.markAsRead(body.uid, folder);
|
|
1093
|
+
break;
|
|
1094
|
+
case "unread":
|
|
1095
|
+
await imap.markAsUnread(body.uid, folder);
|
|
1096
|
+
break;
|
|
1097
|
+
case "flag":
|
|
1098
|
+
await imap.flagEmail(body.uid, folder);
|
|
1099
|
+
break;
|
|
1100
|
+
case "unflag":
|
|
1101
|
+
await imap.unflagEmail(body.uid, folder);
|
|
1102
|
+
break;
|
|
1103
|
+
case "delete":
|
|
1104
|
+
await imap.deleteEmail(body.uid, folder);
|
|
1105
|
+
break;
|
|
1106
|
+
default:
|
|
1107
|
+
return c.json({ error: `Unknown action: ${body.action}` }, 400);
|
|
1108
|
+
}
|
|
1109
|
+
return c.json({ success: true });
|
|
1110
|
+
} catch (error) {
|
|
1111
|
+
console.error("[Email API] /flag error:", error);
|
|
1112
|
+
return c.json(
|
|
1113
|
+
{ error: error instanceof Error ? error.message : "Failed to perform action" },
|
|
1114
|
+
500
|
|
1115
|
+
);
|
|
1116
|
+
} finally {
|
|
1117
|
+
if (imap) {
|
|
1118
|
+
await imap.disconnect().catch(() => {
|
|
1119
|
+
});
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
});
|
|
1123
|
+
|
|
1124
|
+
// src/inputs/api/routes/sdk.ts
|
|
1125
|
+
import { Hono as Hono3 } from "hono";
|
|
1126
|
+
var registeredApps = /* @__PURE__ */ new Map();
|
|
1127
|
+
function generateApiKey() {
|
|
1128
|
+
const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
1129
|
+
const segments = [8, 4, 4, 4, 12];
|
|
1130
|
+
return "osk_" + segments.map((len) => Array.from({ length: len }, () => chars[Math.floor(Math.random() * chars.length)]).join("")).join("-");
|
|
1131
|
+
}
|
|
1132
|
+
async function sdkAuth(c, next) {
|
|
1133
|
+
const authHeader = c.req.header("Authorization");
|
|
1134
|
+
if (!authHeader?.startsWith("Bearer osk_")) {
|
|
1135
|
+
return c.json({ error: "Invalid or missing API key. Register at POST /api/sdk/register" }, 401);
|
|
1136
|
+
}
|
|
1137
|
+
const apiKey = authHeader.slice(7);
|
|
1138
|
+
const app2 = Array.from(registeredApps.values()).find((a) => a.apiKey === apiKey);
|
|
1139
|
+
if (!app2) {
|
|
1140
|
+
return c.json({ error: "Unknown API key. Register at POST /api/sdk/register" }, 401);
|
|
1141
|
+
}
|
|
1142
|
+
app2.lastSeen = /* @__PURE__ */ new Date();
|
|
1143
|
+
c.set("sdkApp", app2);
|
|
1144
|
+
await next();
|
|
1145
|
+
}
|
|
1146
|
+
var sdkRoutes = new Hono3();
|
|
1147
|
+
sdkRoutes.post("/register", async (c) => {
|
|
1148
|
+
try {
|
|
1149
|
+
const body = await c.req.json();
|
|
1150
|
+
if (!body.name || !body.type) {
|
|
1151
|
+
return c.json({ error: "name and type are required" }, 400);
|
|
1152
|
+
}
|
|
1153
|
+
const existing = Array.from(registeredApps.values()).find(
|
|
1154
|
+
(a) => a.name === body.name && a.type === body.type
|
|
1155
|
+
);
|
|
1156
|
+
if (existing) {
|
|
1157
|
+
return c.json({
|
|
1158
|
+
id: existing.id,
|
|
1159
|
+
message: "App already registered. Use your existing API key; it is not re-sent for security."
|
|
1160
|
+
});
|
|
1161
|
+
}
|
|
1162
|
+
const id = crypto.randomUUID();
|
|
1163
|
+
const apiKey = generateApiKey();
|
|
1164
|
+
const app2 = {
|
|
1165
|
+
id,
|
|
1166
|
+
name: body.name,
|
|
1167
|
+
type: body.type,
|
|
1168
|
+
apiKey,
|
|
1169
|
+
callbackUrl: body.callbackUrl,
|
|
1170
|
+
registeredAt: /* @__PURE__ */ new Date(),
|
|
1171
|
+
lastSeen: /* @__PURE__ */ new Date()
|
|
1172
|
+
};
|
|
1173
|
+
registeredApps.set(id, app2);
|
|
1174
|
+
return c.json({
|
|
1175
|
+
id,
|
|
1176
|
+
apiKey,
|
|
1177
|
+
message: "App registered successfully. Use this API key in Authorization: Bearer header.",
|
|
1178
|
+
endpoints: {
|
|
1179
|
+
chat: "POST /api/sdk/chat",
|
|
1180
|
+
notify: "POST /api/sdk/notify",
|
|
1181
|
+
memory_store: "POST /api/sdk/memory",
|
|
1182
|
+
memory_search: "POST /api/sdk/memory/search",
|
|
1183
|
+
tools_list: "GET /api/sdk/tools",
|
|
1184
|
+
tools_execute: "POST /api/sdk/tools/execute",
|
|
1185
|
+
agent_spawn: "POST /api/sdk/agent/spawn",
|
|
1186
|
+
status: "GET /api/sdk/status"
|
|
1187
|
+
}
|
|
1188
|
+
});
|
|
1189
|
+
} catch (error) {
|
|
1190
|
+
return c.json({ error: "Registration failed" }, 500);
|
|
1191
|
+
}
|
|
1192
|
+
});
|
|
1193
|
+
sdkRoutes.use("/*", sdkAuth);
|
|
1194
|
+
sdkRoutes.post("/chat", async (c) => {
|
|
1195
|
+
try {
|
|
1196
|
+
const app2 = c.get("sdkApp");
|
|
1197
|
+
const body = await c.req.json();
|
|
1198
|
+
if (!body.message) {
|
|
1199
|
+
return c.json({ error: "message is required" }, 400);
|
|
1200
|
+
}
|
|
1201
|
+
const messages2 = [];
|
|
1202
|
+
if (body.context) {
|
|
1203
|
+
messages2.push({
|
|
1204
|
+
role: "user",
|
|
1205
|
+
content: `[Context from ${app2.name} (${app2.type})]: ${body.context}`
|
|
1206
|
+
});
|
|
1207
|
+
messages2.push({
|
|
1208
|
+
role: "assistant",
|
|
1209
|
+
content: "Understood, I have the context. How can I help?"
|
|
1210
|
+
});
|
|
1211
|
+
}
|
|
1212
|
+
messages2.push({ role: "user", content: body.message });
|
|
1213
|
+
const toolsUsed = [];
|
|
1214
|
+
const response = body.useTools !== false ? await chatWithTools(messages2, `sdk:${app2.id}`, (tool) => toolsUsed.push(tool), { appType: app2.type }) : await (await import("./brain-SLA474EU.js")).chat(messages2, body.systemPrompt);
|
|
1215
|
+
await storeMemory({
|
|
1216
|
+
content: `[${app2.name}] User asked: ${body.message.slice(0, 200)}`,
|
|
1217
|
+
type: "episodic",
|
|
1218
|
+
importance: 5,
|
|
1219
|
+
userId: `sdk:${app2.id}`,
|
|
1220
|
+
source: `sdk:${app2.name}`,
|
|
1221
|
+
provenance: `sdk:${app2.type}`
|
|
1222
|
+
}).catch(() => {
|
|
1223
|
+
});
|
|
1224
|
+
return c.json({
|
|
1225
|
+
content: response.content,
|
|
1226
|
+
toolsUsed,
|
|
1227
|
+
usage: {
|
|
1228
|
+
inputTokens: response.inputTokens,
|
|
1229
|
+
outputTokens: response.outputTokens
|
|
1230
|
+
},
|
|
1231
|
+
app: app2.name
|
|
1232
|
+
});
|
|
1233
|
+
} catch (error) {
|
|
1234
|
+
console.error("SDK chat error:", error);
|
|
1235
|
+
return c.json({ error: "Chat failed" }, 500);
|
|
1236
|
+
}
|
|
1237
|
+
});
|
|
1238
|
+
sdkRoutes.post("/notify", async (c) => {
|
|
1239
|
+
try {
|
|
1240
|
+
const app2 = c.get("sdkApp");
|
|
1241
|
+
const body = await c.req.json();
|
|
1242
|
+
if (!body.channel || !body.message) {
|
|
1243
|
+
return c.json({ error: "channel and message are required" }, 400);
|
|
1244
|
+
}
|
|
1245
|
+
const sent = [];
|
|
1246
|
+
const prefix = `[${app2.name}] `;
|
|
1247
|
+
const fullMessage = prefix + body.message;
|
|
1248
|
+
const channels = body.channel === "all" ? ["telegram", "discord", "slack"] : [body.channel];
|
|
1249
|
+
for (const ch of channels) {
|
|
1250
|
+
try {
|
|
1251
|
+
switch (ch) {
|
|
1252
|
+
case "telegram": {
|
|
1253
|
+
const { Bot } = await import("grammy");
|
|
1254
|
+
const bot = new Bot(env.TELEGRAM_BOT_TOKEN);
|
|
1255
|
+
await bot.api.sendMessage(env.TELEGRAM_CHAT_ID, fullMessage);
|
|
1256
|
+
sent.push("telegram");
|
|
1257
|
+
break;
|
|
1258
|
+
}
|
|
1259
|
+
case "discord": {
|
|
1260
|
+
const { createDiscordBot } = await import("./discord-NKR3X4AV.js");
|
|
1261
|
+
const discord = createDiscordBot({
|
|
1262
|
+
token: env.DISCORD_BOT_TOKEN || "",
|
|
1263
|
+
clientId: env.DISCORD_CLIENT_ID || ""
|
|
1264
|
+
});
|
|
1265
|
+
if (body.recipient) {
|
|
1266
|
+
await discord.sendMessage(body.recipient, fullMessage);
|
|
1267
|
+
}
|
|
1268
|
+
sent.push("discord");
|
|
1269
|
+
break;
|
|
1270
|
+
}
|
|
1271
|
+
case "slack": {
|
|
1272
|
+
const { createSlackBot } = await import("./slack-P2LFUJUQ.js");
|
|
1273
|
+
const slack = createSlackBot({
|
|
1274
|
+
token: env.SLACK_BOT_TOKEN || "",
|
|
1275
|
+
signingSecret: env.SLACK_SIGNING_SECRET || ""
|
|
1276
|
+
});
|
|
1277
|
+
if (body.recipient) {
|
|
1278
|
+
await slack.sendMessage(body.recipient, fullMessage);
|
|
1279
|
+
}
|
|
1280
|
+
sent.push("slack");
|
|
1281
|
+
break;
|
|
1282
|
+
}
|
|
1283
|
+
case "email": {
|
|
1284
|
+
const { SmtpClient: SmtpClient2 } = await import("./email-EAQNULVD.js");
|
|
1285
|
+
const smtp = new SmtpClient2({
|
|
1286
|
+
host: env.EMAIL_SMTP_HOST || "localhost",
|
|
1287
|
+
port: env.EMAIL_SMTP_PORT || 587,
|
|
1288
|
+
secure: env.EMAIL_SMTP_SECURE || false,
|
|
1289
|
+
auth: { user: env.EMAIL_USER || "", pass: env.EMAIL_PASSWORD || "" }
|
|
1290
|
+
});
|
|
1291
|
+
await smtp.send({
|
|
1292
|
+
to: body.recipient || "",
|
|
1293
|
+
subject: `${app2.name} Notification`,
|
|
1294
|
+
text: body.message
|
|
1295
|
+
});
|
|
1296
|
+
sent.push("email");
|
|
1297
|
+
break;
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
} catch (err) {
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
return c.json({ sent, message: `Notification sent via: ${sent.join(", ") || "none (no channels configured)"}` });
|
|
1304
|
+
} catch (error) {
|
|
1305
|
+
return c.json({ error: "Notification failed" }, 500);
|
|
1306
|
+
}
|
|
1307
|
+
});
|
|
1308
|
+
sdkRoutes.post("/memory", async (c) => {
|
|
1309
|
+
try {
|
|
1310
|
+
const app2 = c.get("sdkApp");
|
|
1311
|
+
const body = await c.req.json();
|
|
1312
|
+
if (!body.content) {
|
|
1313
|
+
return c.json({ error: "content is required" }, 400);
|
|
1314
|
+
}
|
|
1315
|
+
const memory = await storeMemory({
|
|
1316
|
+
content: body.content,
|
|
1317
|
+
type: body.type || "semantic",
|
|
1318
|
+
importance: body.importance || 5,
|
|
1319
|
+
userId: `sdk:${app2.id}`,
|
|
1320
|
+
source: `sdk:${app2.name}`,
|
|
1321
|
+
provenance: `sdk:${app2.type}`
|
|
1322
|
+
});
|
|
1323
|
+
return c.json(memory);
|
|
1324
|
+
} catch (error) {
|
|
1325
|
+
return c.json({ error: "Memory store failed" }, 500);
|
|
1326
|
+
}
|
|
1327
|
+
});
|
|
1328
|
+
sdkRoutes.post("/memory/search", async (c) => {
|
|
1329
|
+
try {
|
|
1330
|
+
const app2 = c.get("sdkApp");
|
|
1331
|
+
const body = await c.req.json();
|
|
1332
|
+
if (!body.query) {
|
|
1333
|
+
return c.json({ error: "query is required" }, 400);
|
|
1334
|
+
}
|
|
1335
|
+
const userId = body.crossApp ? void 0 : `sdk:${app2.id}`;
|
|
1336
|
+
const results = await searchMemories(body.query, userId, body.limit || 5);
|
|
1337
|
+
return c.json(results);
|
|
1338
|
+
} catch (error) {
|
|
1339
|
+
return c.json({ error: "Memory search failed" }, 500);
|
|
1340
|
+
}
|
|
1341
|
+
});
|
|
1342
|
+
sdkRoutes.get("/tools", (c) => {
|
|
1343
|
+
const app2 = c.get("sdkApp");
|
|
1344
|
+
const ordered = prioritizeTools(TOOLS, app2.type);
|
|
1345
|
+
const profile = getAppProfile(app2.type);
|
|
1346
|
+
const prioritySet = new Set(profile.priorityTools);
|
|
1347
|
+
const toolList = ordered.map((t) => ({
|
|
1348
|
+
name: t.name,
|
|
1349
|
+
description: t.description,
|
|
1350
|
+
priority: prioritySet.has(t.name)
|
|
1351
|
+
}));
|
|
1352
|
+
return c.json({ tools: toolList, count: toolList.length, appType: app2.type });
|
|
1353
|
+
});
|
|
1354
|
+
sdkRoutes.post("/tools/execute", async (c) => {
|
|
1355
|
+
try {
|
|
1356
|
+
const body = await c.req.json();
|
|
1357
|
+
if (!body.tool || !body.input) {
|
|
1358
|
+
return c.json({ error: "tool and input are required" }, 400);
|
|
1359
|
+
}
|
|
1360
|
+
const result = await executeTool(body.tool, body.input);
|
|
1361
|
+
return c.json({ tool: body.tool, result });
|
|
1362
|
+
} catch (error) {
|
|
1363
|
+
return c.json({ error: "Tool execution failed" }, 500);
|
|
1364
|
+
}
|
|
1365
|
+
});
|
|
1366
|
+
sdkRoutes.post("/agent/spawn", async (c) => {
|
|
1367
|
+
try {
|
|
1368
|
+
const app2 = c.get("sdkApp");
|
|
1369
|
+
const body = await c.req.json();
|
|
1370
|
+
if (!body.type || !body.task) {
|
|
1371
|
+
return c.json({ error: "type and task are required" }, 400);
|
|
1372
|
+
}
|
|
1373
|
+
const agentPrompt = `As a ${body.type} agent for ${app2.name}, complete this task: ${body.task}${body.context ? `
|
|
1374
|
+
|
|
1375
|
+
Context: ${body.context}` : ""}`;
|
|
1376
|
+
const messages2 = [{ role: "user", content: agentPrompt }];
|
|
1377
|
+
const toolsUsed = [];
|
|
1378
|
+
const response = await chatWithTools(messages2, `sdk:${app2.id}:agent:${body.type}`, (tool) => toolsUsed.push(tool), { appType: app2.type });
|
|
1379
|
+
return c.json({
|
|
1380
|
+
agent: body.type,
|
|
1381
|
+
result: response.content,
|
|
1382
|
+
toolsUsed,
|
|
1383
|
+
usage: {
|
|
1384
|
+
inputTokens: response.inputTokens,
|
|
1385
|
+
outputTokens: response.outputTokens
|
|
1386
|
+
}
|
|
1387
|
+
});
|
|
1388
|
+
} catch (error) {
|
|
1389
|
+
return c.json({ error: "Agent spawn failed" }, 500);
|
|
1390
|
+
}
|
|
1391
|
+
});
|
|
1392
|
+
sdkRoutes.get("/status", (c) => {
|
|
1393
|
+
const app2 = c.get("sdkApp");
|
|
1394
|
+
const allApps = Array.from(registeredApps.values()).map((a) => ({
|
|
1395
|
+
id: a.id,
|
|
1396
|
+
name: a.name,
|
|
1397
|
+
type: a.type,
|
|
1398
|
+
registeredAt: a.registeredAt,
|
|
1399
|
+
lastSeen: a.lastSeen
|
|
1400
|
+
}));
|
|
1401
|
+
return c.json({
|
|
1402
|
+
opensentinel: {
|
|
1403
|
+
status: "online",
|
|
1404
|
+
version: "2.2.1",
|
|
1405
|
+
uptime: process.uptime()
|
|
1406
|
+
},
|
|
1407
|
+
currentApp: {
|
|
1408
|
+
id: app2.id,
|
|
1409
|
+
name: app2.name,
|
|
1410
|
+
type: app2.type
|
|
1411
|
+
},
|
|
1412
|
+
registeredApps: allApps,
|
|
1413
|
+
tools: TOOLS.length
|
|
1414
|
+
});
|
|
1415
|
+
});
|
|
1416
|
+
|
|
1417
|
+
// src/inputs/api/routes/admin.ts
|
|
1418
|
+
import { Hono as Hono4 } from "hono";
|
|
1419
|
+
|
|
1420
|
+
// src/core/security/rate-limiter.ts
|
|
1421
|
+
import Redis from "ioredis";
|
|
1422
|
+
var redis = new Redis(env.REDIS_URL, {
|
|
1423
|
+
maxRetriesPerRequest: 3
|
|
1424
|
+
});
|
|
1425
|
+
var DEFAULT_LIMITS = {
|
|
1426
|
+
"api/chat": { windowMs: 6e4, maxRequests: 30 },
|
|
1427
|
+
"api/chat/tools": { windowMs: 6e4, maxRequests: 20 },
|
|
1428
|
+
"api/ask": { windowMs: 6e4, maxRequests: 30 },
|
|
1429
|
+
"tool/shell": { windowMs: 6e4, maxRequests: 10 },
|
|
1430
|
+
"tool/browser": { windowMs: 6e4, maxRequests: 10 },
|
|
1431
|
+
"tool/file_write": { windowMs: 6e4, maxRequests: 20 },
|
|
1432
|
+
"agent/spawn": { windowMs: 36e5, maxRequests: 5 },
|
|
1433
|
+
// 5 agents per hour
|
|
1434
|
+
default: { windowMs: 6e4, maxRequests: 60 }
|
|
1435
|
+
};
|
|
1436
|
+
function getKey(identifier, endpoint) {
|
|
1437
|
+
return `ratelimit:${identifier}:${endpoint}`;
|
|
1438
|
+
}
|
|
1439
|
+
async function checkRateLimit(identifier, endpoint, customConfig) {
|
|
1440
|
+
const config = customConfig || DEFAULT_LIMITS[endpoint] || DEFAULT_LIMITS.default;
|
|
1441
|
+
const { windowMs, maxRequests } = config;
|
|
1442
|
+
const key = getKey(identifier, endpoint);
|
|
1443
|
+
const now = Date.now();
|
|
1444
|
+
try {
|
|
1445
|
+
const multi = redis.multi();
|
|
1446
|
+
multi.zremrangebyscore(key, 0, now - windowMs);
|
|
1447
|
+
multi.zcard(key);
|
|
1448
|
+
multi.zadd(key, now.toString(), `${now}:${Math.random()}`);
|
|
1449
|
+
multi.pexpire(key, windowMs);
|
|
1450
|
+
const results = await multi.exec();
|
|
1451
|
+
if (!results) {
|
|
1452
|
+
console.warn("[RateLimiter] Redis transaction failed, allowing request");
|
|
1453
|
+
return {
|
|
1454
|
+
allowed: true,
|
|
1455
|
+
remaining: maxRequests,
|
|
1456
|
+
resetAt: new Date(now + windowMs)
|
|
1457
|
+
};
|
|
1458
|
+
}
|
|
1459
|
+
const currentCount = results[1]?.[1] || 0;
|
|
1460
|
+
const resetAt = new Date(now + windowMs);
|
|
1461
|
+
if (currentCount >= maxRequests) {
|
|
1462
|
+
await redis.zrem(key, `${now}:${Math.random()}`);
|
|
1463
|
+
return {
|
|
1464
|
+
allowed: false,
|
|
1465
|
+
remaining: 0,
|
|
1466
|
+
resetAt,
|
|
1467
|
+
retryAfterMs: windowMs - (now - await getWindowStart(key, now, windowMs))
|
|
1468
|
+
};
|
|
1469
|
+
}
|
|
1470
|
+
return {
|
|
1471
|
+
allowed: true,
|
|
1472
|
+
remaining: maxRequests - currentCount - 1,
|
|
1473
|
+
resetAt
|
|
1474
|
+
};
|
|
1475
|
+
} catch (error) {
|
|
1476
|
+
console.error("[RateLimiter] Error checking rate limit:", error);
|
|
1477
|
+
return {
|
|
1478
|
+
allowed: true,
|
|
1479
|
+
remaining: maxRequests,
|
|
1480
|
+
resetAt: new Date(now + windowMs)
|
|
1481
|
+
};
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
async function getWindowStart(key, now, windowMs) {
|
|
1485
|
+
const oldest = await redis.zrange(key, 0, 0, "WITHSCORES");
|
|
1486
|
+
if (oldest.length >= 2) {
|
|
1487
|
+
return parseInt(oldest[1], 10);
|
|
1488
|
+
}
|
|
1489
|
+
return now - windowMs;
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
// src/inputs/api/routes/admin.ts
|
|
1493
|
+
var adminRouter = new Hono4();
|
|
1494
|
+
async function requireAdminAuth(c, next) {
|
|
1495
|
+
const gatewayToken = getGatewayToken();
|
|
1496
|
+
if (!gatewayToken) {
|
|
1497
|
+
return next();
|
|
1498
|
+
}
|
|
1499
|
+
const apiKey = c.req.header("x-api-key");
|
|
1500
|
+
if (apiKey) {
|
|
1501
|
+
const rateLimitResult = await checkRateLimit(apiKey, "api/admin");
|
|
1502
|
+
if (!rateLimitResult.allowed) {
|
|
1503
|
+
return c.json(
|
|
1504
|
+
{ error: "Rate limit exceeded", retryAfter: rateLimitResult.retryAfterMs },
|
|
1505
|
+
429
|
|
1506
|
+
);
|
|
1507
|
+
}
|
|
1508
|
+
return next();
|
|
1509
|
+
}
|
|
1510
|
+
const authHeader = c.req.header("Authorization");
|
|
1511
|
+
if (authHeader?.startsWith("Bearer ")) {
|
|
1512
|
+
const token = authHeader.slice(7);
|
|
1513
|
+
if (timingSafeEqual(token, gatewayToken)) {
|
|
1514
|
+
return next();
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
return c.json({ error: "API key required" }, 401);
|
|
1518
|
+
}
|
|
1519
|
+
adminRouter.use("*", requireAdminAuth);
|
|
1520
|
+
adminRouter.get("/audit-logs", async (c) => {
|
|
1521
|
+
try {
|
|
1522
|
+
const url = new URL(c.req.url);
|
|
1523
|
+
const options = {};
|
|
1524
|
+
const userId = url.searchParams.get("userId");
|
|
1525
|
+
if (userId) options.userId = userId;
|
|
1526
|
+
const action = url.searchParams.get("action");
|
|
1527
|
+
if (action) options.action = action;
|
|
1528
|
+
const resource = url.searchParams.get("resource");
|
|
1529
|
+
if (resource) options.resource = resource;
|
|
1530
|
+
const startDate = url.searchParams.get("startDate");
|
|
1531
|
+
if (startDate) options.startDate = new Date(startDate);
|
|
1532
|
+
const endDate = url.searchParams.get("endDate");
|
|
1533
|
+
if (endDate) options.endDate = new Date(endDate);
|
|
1534
|
+
const limit = url.searchParams.get("limit");
|
|
1535
|
+
options.limit = limit ? Math.min(parseInt(limit, 10), 500) : 100;
|
|
1536
|
+
const offset = url.searchParams.get("offset");
|
|
1537
|
+
options.offset = offset ? parseInt(offset, 10) : 0;
|
|
1538
|
+
const logs = await queryAuditLogs(options);
|
|
1539
|
+
return c.json({
|
|
1540
|
+
success: true,
|
|
1541
|
+
logs,
|
|
1542
|
+
count: logs.length,
|
|
1543
|
+
limit: options.limit,
|
|
1544
|
+
offset: options.offset
|
|
1545
|
+
});
|
|
1546
|
+
} catch (err) {
|
|
1547
|
+
return c.json({ error: err.message || "Failed to query audit logs" }, 500);
|
|
1548
|
+
}
|
|
1549
|
+
});
|
|
1550
|
+
adminRouter.get("/audit-logs/integrity", async (c) => {
|
|
1551
|
+
try {
|
|
1552
|
+
const integrity = await getAuditChainIntegrity();
|
|
1553
|
+
return c.json({ success: true, ...integrity });
|
|
1554
|
+
} catch (err) {
|
|
1555
|
+
return c.json({ error: err.message || "Failed to check integrity" }, 500);
|
|
1556
|
+
}
|
|
1557
|
+
});
|
|
1558
|
+
adminRouter.get("/incidents", async (c) => {
|
|
1559
|
+
try {
|
|
1560
|
+
const url = new URL(c.req.url);
|
|
1561
|
+
const limit = Math.min(parseInt(url.searchParams.get("limit") || "50", 10), 200);
|
|
1562
|
+
const incidents = await queryAuditLogs({
|
|
1563
|
+
action: "error",
|
|
1564
|
+
limit
|
|
1565
|
+
});
|
|
1566
|
+
return c.json({
|
|
1567
|
+
success: true,
|
|
1568
|
+
incidents,
|
|
1569
|
+
count: incidents.length
|
|
1570
|
+
});
|
|
1571
|
+
} catch (err) {
|
|
1572
|
+
return c.json({ error: err.message || "Failed to query incidents" }, 500);
|
|
1573
|
+
}
|
|
1574
|
+
});
|
|
1575
|
+
var admin_default = adminRouter;
|
|
1576
|
+
|
|
1577
|
+
// src/inputs/api/routes/brain.ts
|
|
1578
|
+
import { Hono as Hono5 } from "hono";
|
|
1579
|
+
var brainRouter = new Hono5();
|
|
1580
|
+
brainRouter.get("/status", (c) => {
|
|
1581
|
+
return c.json(brainTelemetry.getStatus());
|
|
1582
|
+
});
|
|
1583
|
+
brainRouter.get("/activity", (c) => {
|
|
1584
|
+
const limit = parseInt(c.req.query("limit") || "100");
|
|
1585
|
+
return c.json(brainTelemetry.getActivity(Math.min(limit, 500)));
|
|
1586
|
+
});
|
|
1587
|
+
brainRouter.get("/scores", async (c) => {
|
|
1588
|
+
await costTracker.loadFromDb();
|
|
1589
|
+
return c.json(brainTelemetry.getScores());
|
|
1590
|
+
});
|
|
1591
|
+
brainRouter.get("/agents", async (c) => {
|
|
1592
|
+
const userId = c.req.query("userId");
|
|
1593
|
+
try {
|
|
1594
|
+
const { getUserAgents, getAllAgents } = await import("./agent-manager-7N7REQZQ.js");
|
|
1595
|
+
const agents = userId ? await getUserAgents(userId, void 0, 20) : await getAllAgents(void 0, 50);
|
|
1596
|
+
return c.json(agents);
|
|
1597
|
+
} catch {
|
|
1598
|
+
return c.json([]);
|
|
1599
|
+
}
|
|
1600
|
+
});
|
|
1601
|
+
brainRouter.get("/cost/forecast", (c) => {
|
|
1602
|
+
const days = parseInt(c.req.query("days") || "7");
|
|
1603
|
+
return c.json({
|
|
1604
|
+
forecast: costTracker.getForecast(days),
|
|
1605
|
+
trend: costTracker.getCostTrend(),
|
|
1606
|
+
estimatedMonthly: costTracker.getEstimatedMonthlyCost()
|
|
1607
|
+
});
|
|
1608
|
+
});
|
|
1609
|
+
brainRouter.get("/memory-graph", async (c) => {
|
|
1610
|
+
const entityId = c.req.query("entityId");
|
|
1611
|
+
const limit = parseInt(c.req.query("limit") || "50");
|
|
1612
|
+
try {
|
|
1613
|
+
const memories2 = await searchMemories("", void 0, limit);
|
|
1614
|
+
const nodes = [];
|
|
1615
|
+
const edges = [];
|
|
1616
|
+
for (const mem of memories2) {
|
|
1617
|
+
nodes.push({
|
|
1618
|
+
id: `mem-${mem.id}`,
|
|
1619
|
+
name: (mem.content || "").slice(0, 60) + ((mem.content || "").length > 60 ? "..." : ""),
|
|
1620
|
+
type: "memory",
|
|
1621
|
+
importance: (mem.importance ?? 5) * 10,
|
|
1622
|
+
// Scale 1-10 to 10-100
|
|
1623
|
+
content: mem.content || "",
|
|
1624
|
+
createdAt: mem.createdAt?.toISOString?.() || (/* @__PURE__ */ new Date()).toISOString()
|
|
1625
|
+
});
|
|
1626
|
+
}
|
|
1627
|
+
if (entityId) {
|
|
1628
|
+
try {
|
|
1629
|
+
const { db: db2 } = await import("./db-LRIOKQBO.js");
|
|
1630
|
+
const { graphEntities: graphEntities2 } = await import("./schema-ALJ67YVG.js");
|
|
1631
|
+
const { eq: eq5 } = await import("drizzle-orm");
|
|
1632
|
+
const [entity] = await db2.select().from(graphEntities2).where(eq5(graphEntities2.id, entityId)).limit(1);
|
|
1633
|
+
if (entity) {
|
|
1634
|
+
nodes.push({
|
|
1635
|
+
id: entity.id,
|
|
1636
|
+
name: entity.name,
|
|
1637
|
+
type: entity.type,
|
|
1638
|
+
importance: entity.importance ?? 50,
|
|
1639
|
+
content: entity.description || "",
|
|
1640
|
+
createdAt: entity.createdAt?.toISOString?.() || (/* @__PURE__ */ new Date()).toISOString()
|
|
1641
|
+
});
|
|
1642
|
+
for (const mem of memories2) {
|
|
1643
|
+
const content = (mem.content || "").toLowerCase();
|
|
1644
|
+
if (content.includes(entity.name.toLowerCase()) || (entity.aliases || []).some(
|
|
1645
|
+
(a) => content.includes(a.toLowerCase())
|
|
1646
|
+
)) {
|
|
1647
|
+
edges.push({
|
|
1648
|
+
source: `mem-${mem.id}`,
|
|
1649
|
+
target: entity.id,
|
|
1650
|
+
type: "relates_to",
|
|
1651
|
+
strength: 60
|
|
1652
|
+
});
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
} catch {
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
1660
|
+
for (let j = i + 1; j < nodes.length; j++) {
|
|
1661
|
+
if (nodes[i].type === "memory" && nodes[j].type === "memory" && nodes[i].content && nodes[j].content) {
|
|
1662
|
+
const words1 = new Set(nodes[i].content.toLowerCase().split(/\s+/).filter((w) => w.length > 4));
|
|
1663
|
+
const words2 = new Set(nodes[j].content.toLowerCase().split(/\s+/).filter((w) => w.length > 4));
|
|
1664
|
+
const overlap = [...words1].filter((w) => words2.has(w)).length;
|
|
1665
|
+
if (overlap >= 3) {
|
|
1666
|
+
edges.push({
|
|
1667
|
+
source: nodes[i].id,
|
|
1668
|
+
target: nodes[j].id,
|
|
1669
|
+
type: "related",
|
|
1670
|
+
strength: Math.min(100, overlap * 15)
|
|
1671
|
+
});
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
return c.json({ nodes, edges });
|
|
1677
|
+
} catch (error) {
|
|
1678
|
+
return c.json({ nodes: [], edges: [], error: "Failed to build memory graph" }, 500);
|
|
1679
|
+
}
|
|
1680
|
+
});
|
|
1681
|
+
brainRouter.get("/agents/catalog", async (c) => {
|
|
1682
|
+
try {
|
|
1683
|
+
const { AGENT_TOOL_PERMISSIONS } = await import("./agent-types-2T4PXLFQ.js");
|
|
1684
|
+
const configs = await Promise.all([
|
|
1685
|
+
import("./research-agent-WCRSY3UZ.js").then((m) => m.RESEARCH_AGENT_CONFIG),
|
|
1686
|
+
import("./coding-agent-DESSU3AC.js").then((m) => m.CODING_AGENT_CONFIG),
|
|
1687
|
+
import("./writing-agent-VDGLNOGO.js").then((m) => m.WRITING_AGENT_CONFIG),
|
|
1688
|
+
import("./analysis-agent-JWN2GXYE.js").then((m) => m.ANALYSIS_AGENT_CONFIG),
|
|
1689
|
+
import("./osint-agent-RL5XPBRQ.js").then((m) => m.OSINT_AGENT_CONFIG)
|
|
1690
|
+
]);
|
|
1691
|
+
const catalog = configs.map((cfg) => ({
|
|
1692
|
+
type: cfg.type,
|
|
1693
|
+
name: cfg.name,
|
|
1694
|
+
description: cfg.description,
|
|
1695
|
+
tools: AGENT_TOOL_PERMISSIONS[cfg.type] || [],
|
|
1696
|
+
settings: cfg.settings || {}
|
|
1697
|
+
}));
|
|
1698
|
+
return c.json(catalog);
|
|
1699
|
+
} catch {
|
|
1700
|
+
return c.json([]);
|
|
1701
|
+
}
|
|
1702
|
+
});
|
|
1703
|
+
brainRouter.post("/agents/spawn", async (c) => {
|
|
1704
|
+
try {
|
|
1705
|
+
const { type, objective } = await c.req.json();
|
|
1706
|
+
if (!type || !objective) {
|
|
1707
|
+
return c.json({ error: "type and objective are required" }, 400);
|
|
1708
|
+
}
|
|
1709
|
+
const { db: db2 } = await import("./db-LRIOKQBO.js");
|
|
1710
|
+
const { users: users2 } = await import("./schema-ALJ67YVG.js");
|
|
1711
|
+
let systemUser = await db2.select().from(users2).limit(1);
|
|
1712
|
+
if (systemUser.length === 0) {
|
|
1713
|
+
systemUser = await db2.insert(users2).values({ name: "Dashboard" }).returning();
|
|
1714
|
+
}
|
|
1715
|
+
const { spawnAgent } = await import("./agent-manager-7N7REQZQ.js");
|
|
1716
|
+
const agent = await spawnAgent({
|
|
1717
|
+
type,
|
|
1718
|
+
objective,
|
|
1719
|
+
userId: systemUser[0].id
|
|
1720
|
+
});
|
|
1721
|
+
return c.json(agent, 201);
|
|
1722
|
+
} catch (error) {
|
|
1723
|
+
return c.json({ error: error?.message || "Failed to spawn agent" }, 500);
|
|
1724
|
+
}
|
|
1725
|
+
});
|
|
1726
|
+
brainRouter.post("/agents/seed", async (c) => {
|
|
1727
|
+
try {
|
|
1728
|
+
const { db: db2 } = await import("./db-LRIOKQBO.js");
|
|
1729
|
+
const { subAgents, agentProgress, users: users2 } = await import("./schema-ALJ67YVG.js");
|
|
1730
|
+
let systemUser = await db2.select().from(users2).limit(1);
|
|
1731
|
+
if (systemUser.length === 0) {
|
|
1732
|
+
systemUser = await db2.insert(users2).values({
|
|
1733
|
+
name: "System"
|
|
1734
|
+
}).returning();
|
|
1735
|
+
}
|
|
1736
|
+
const userId = systemUser[0].id;
|
|
1737
|
+
const now = Date.now();
|
|
1738
|
+
const tasks = [
|
|
1739
|
+
{
|
|
1740
|
+
userId,
|
|
1741
|
+
type: "research",
|
|
1742
|
+
name: "Research Agent",
|
|
1743
|
+
status: "completed",
|
|
1744
|
+
objective: "Research current best practices for PostgreSQL query optimization and indexing strategies",
|
|
1745
|
+
tokenBudget: 5e4,
|
|
1746
|
+
tokensUsed: 32450,
|
|
1747
|
+
createdAt: new Date(now - 3 * 36e5),
|
|
1748
|
+
result: { success: true, summary: "Compiled 12 optimization strategies including partial indexes, covering indexes, and query plan analysis. Key findings: BRIN indexes for time-series data, GIN for JSONB columns, and composite indexes for multi-column queries.", durationMs: 45200 }
|
|
1749
|
+
},
|
|
1750
|
+
{
|
|
1751
|
+
userId,
|
|
1752
|
+
type: "coding",
|
|
1753
|
+
name: "Coding Agent",
|
|
1754
|
+
status: "completed",
|
|
1755
|
+
objective: "Implement rate limiting middleware for the API with configurable windows and Redis backing",
|
|
1756
|
+
tokenBudget: 8e4,
|
|
1757
|
+
tokensUsed: 61200,
|
|
1758
|
+
createdAt: new Date(now - 6 * 36e5),
|
|
1759
|
+
result: { success: true, summary: "Implemented sliding window rate limiter using Redis ZSET. Supports per-user and per-IP limits with configurable windows (1s, 1m, 1h, 1d). Added X-RateLimit headers and 429 responses.", durationMs: 128400 }
|
|
1760
|
+
},
|
|
1761
|
+
{
|
|
1762
|
+
userId,
|
|
1763
|
+
type: "analysis",
|
|
1764
|
+
name: "Analysis Agent",
|
|
1765
|
+
status: "completed",
|
|
1766
|
+
objective: "Analyze token usage patterns over the last 30 days and identify cost optimization opportunities",
|
|
1767
|
+
tokenBudget: 4e4,
|
|
1768
|
+
tokensUsed: 28900,
|
|
1769
|
+
createdAt: new Date(now - 12 * 36e5),
|
|
1770
|
+
result: { success: true, summary: "Found 3 key optimization opportunities: 1) System prompts can be shortened by 40% with prompt caching, 2) Research queries could use haiku for initial filtering, 3) Redundant context in multi-turn conversations accounts for 22% of input tokens.", durationMs: 67800 }
|
|
1771
|
+
},
|
|
1772
|
+
{
|
|
1773
|
+
userId,
|
|
1774
|
+
type: "writing",
|
|
1775
|
+
name: "Writing Agent",
|
|
1776
|
+
status: "completed",
|
|
1777
|
+
objective: "Generate API documentation for the webhook and scheduler endpoints",
|
|
1778
|
+
tokenBudget: 3e4,
|
|
1779
|
+
tokensUsed: 18700,
|
|
1780
|
+
createdAt: new Date(now - 24 * 36e5),
|
|
1781
|
+
result: { success: true, summary: "Generated OpenAPI 3.1 documentation for 8 endpoints across scheduler and webhook routes. Includes request/response schemas, authentication requirements, and usage examples.", durationMs: 34500 }
|
|
1782
|
+
},
|
|
1783
|
+
{
|
|
1784
|
+
userId,
|
|
1785
|
+
type: "research",
|
|
1786
|
+
name: "Research Agent",
|
|
1787
|
+
status: "completed",
|
|
1788
|
+
objective: "Gather public information about recent AI security vulnerabilities and prompt injection techniques",
|
|
1789
|
+
tokenBudget: 6e4,
|
|
1790
|
+
tokensUsed: 45300,
|
|
1791
|
+
createdAt: new Date(now - 48 * 36e5),
|
|
1792
|
+
result: { success: true, summary: "Compiled report on 15 known prompt injection vectors including indirect injection via retrieved content, multi-modal attacks through images, and tool-use exploitation. Recommended 6 mitigation strategies.", durationMs: 89200 }
|
|
1793
|
+
},
|
|
1794
|
+
{
|
|
1795
|
+
userId,
|
|
1796
|
+
type: "research",
|
|
1797
|
+
name: "Research Agent",
|
|
1798
|
+
status: "failed",
|
|
1799
|
+
objective: "Research pgvector HNSW vs IVFFlat index performance for RAG memory retrieval",
|
|
1800
|
+
tokenBudget: 5e4,
|
|
1801
|
+
tokensUsed: 12800,
|
|
1802
|
+
createdAt: new Date(now - 2 * 36e5),
|
|
1803
|
+
result: { success: false, error: "Token budget exhausted during benchmark phase. Partial results: HNSW shows better recall at higher ef_search values but IVFFlat index build is 3x faster.", durationMs: 22100 }
|
|
1804
|
+
},
|
|
1805
|
+
{
|
|
1806
|
+
userId,
|
|
1807
|
+
type: "coding",
|
|
1808
|
+
name: "Coding Agent",
|
|
1809
|
+
status: "running",
|
|
1810
|
+
objective: "Build automated test suite for the MCP server integration layer",
|
|
1811
|
+
tokenBudget: 1e5,
|
|
1812
|
+
tokensUsed: 34200,
|
|
1813
|
+
createdAt: new Date(now - 18e5),
|
|
1814
|
+
result: null
|
|
1815
|
+
},
|
|
1816
|
+
{
|
|
1817
|
+
userId,
|
|
1818
|
+
type: "analysis",
|
|
1819
|
+
name: "Analysis Agent",
|
|
1820
|
+
status: "pending",
|
|
1821
|
+
objective: "Analyze error patterns in the last 7 days and correlate with deployment events",
|
|
1822
|
+
tokenBudget: 4e4,
|
|
1823
|
+
tokensUsed: 0,
|
|
1824
|
+
createdAt: new Date(now - 3e5),
|
|
1825
|
+
result: null
|
|
1826
|
+
}
|
|
1827
|
+
];
|
|
1828
|
+
const inserted = [];
|
|
1829
|
+
for (const task of tasks) {
|
|
1830
|
+
const [row] = await db2.insert(subAgents).values(task).returning();
|
|
1831
|
+
inserted.push(row);
|
|
1832
|
+
}
|
|
1833
|
+
const runningTask = inserted.find((t) => t.status === "running");
|
|
1834
|
+
if (runningTask) {
|
|
1835
|
+
await db2.insert(agentProgress).values([
|
|
1836
|
+
{ agentId: runningTask.id, step: 1, description: "Analyzing existing MCP client code and interfaces", status: "completed" },
|
|
1837
|
+
{ agentId: runningTask.id, step: 2, description: "Generating mock MCP server for testing", status: "completed" },
|
|
1838
|
+
{ agentId: runningTask.id, step: 3, description: "Writing connection lifecycle tests", status: "running" },
|
|
1839
|
+
{ agentId: runningTask.id, step: 4, description: "Writing tool discovery and execution tests", status: "pending" },
|
|
1840
|
+
{ agentId: runningTask.id, step: 5, description: "Writing error handling and retry tests", status: "pending" }
|
|
1841
|
+
]);
|
|
1842
|
+
}
|
|
1843
|
+
const researchTask = inserted.find((t) => t.status === "completed" && t.type === "research");
|
|
1844
|
+
if (researchTask) {
|
|
1845
|
+
await db2.insert(agentProgress).values([
|
|
1846
|
+
{ agentId: researchTask.id, step: 1, description: "Surveying PostgreSQL documentation and performance guides", status: "completed" },
|
|
1847
|
+
{ agentId: researchTask.id, step: 2, description: "Analyzing index types: B-tree, Hash, GIN, GiST, BRIN", status: "completed" },
|
|
1848
|
+
{ agentId: researchTask.id, step: 3, description: "Reviewing EXPLAIN ANALYZE best practices", status: "completed" },
|
|
1849
|
+
{ agentId: researchTask.id, step: 4, description: "Compiling optimization recommendations", status: "completed" }
|
|
1850
|
+
]);
|
|
1851
|
+
}
|
|
1852
|
+
return c.json({ success: true, seeded: inserted.length });
|
|
1853
|
+
} catch (err) {
|
|
1854
|
+
return c.json({ error: err?.message || "Failed to seed tasks" }, 500);
|
|
1855
|
+
}
|
|
1856
|
+
});
|
|
1857
|
+
brainRouter.delete("/agents/history", async (c) => {
|
|
1858
|
+
try {
|
|
1859
|
+
const { db: db2 } = await import("./db-LRIOKQBO.js");
|
|
1860
|
+
const { subAgents } = await import("./schema-ALJ67YVG.js");
|
|
1861
|
+
const { inArray } = await import("drizzle-orm");
|
|
1862
|
+
await db2.delete(subAgents).where(inArray(subAgents.status, ["completed", "failed", "cancelled"]));
|
|
1863
|
+
return c.json({ success: true });
|
|
1864
|
+
} catch (err) {
|
|
1865
|
+
return c.json({ error: err?.message || "Failed to clear task history" }, 500);
|
|
1866
|
+
}
|
|
1867
|
+
});
|
|
1868
|
+
brainRouter.delete("/activity", (c) => {
|
|
1869
|
+
brainTelemetry.clearActivity?.();
|
|
1870
|
+
return c.json({ success: true });
|
|
1871
|
+
});
|
|
1872
|
+
var brain_default = brainRouter;
|
|
1873
|
+
|
|
1874
|
+
// src/inputs/api/routes/scheduler.ts
|
|
1875
|
+
import { Hono as Hono6 } from "hono";
|
|
1876
|
+
var schedulerRouter = new Hono6();
|
|
1877
|
+
schedulerRouter.get("/jobs", async (c) => {
|
|
1878
|
+
try {
|
|
1879
|
+
const { taskQueue, maintenanceQueue } = await import("./scheduler-CA5UNHZV.js");
|
|
1880
|
+
const [taskJobs, maintenanceJobs] = await Promise.allSettled([
|
|
1881
|
+
taskQueue?.getRepeatableJobs?.() ?? Promise.resolve([]),
|
|
1882
|
+
maintenanceQueue?.getRepeatableJobs?.() ?? Promise.resolve([])
|
|
1883
|
+
]);
|
|
1884
|
+
const jobs = [
|
|
1885
|
+
...taskJobs.status === "fulfilled" ? taskJobs.value : [],
|
|
1886
|
+
...maintenanceJobs.status === "fulfilled" ? maintenanceJobs.value : []
|
|
1887
|
+
];
|
|
1888
|
+
return c.json(jobs);
|
|
1889
|
+
} catch (error) {
|
|
1890
|
+
return c.json([], 200);
|
|
1891
|
+
}
|
|
1892
|
+
});
|
|
1893
|
+
schedulerRouter.get("/stats", async (c) => {
|
|
1894
|
+
try {
|
|
1895
|
+
const { getQueueStats } = await import("./scheduler-CA5UNHZV.js");
|
|
1896
|
+
const stats = await getQueueStats();
|
|
1897
|
+
return c.json(stats);
|
|
1898
|
+
} catch (error) {
|
|
1899
|
+
return c.json({ waiting: 0, active: 0, completed: 0, failed: 0, delayed: 0 });
|
|
1900
|
+
}
|
|
1901
|
+
});
|
|
1902
|
+
schedulerRouter.post("/jobs", async (c) => {
|
|
1903
|
+
try {
|
|
1904
|
+
const { name, pattern, task } = await c.req.json();
|
|
1905
|
+
if (!name || !pattern) {
|
|
1906
|
+
return c.json({ error: "name and pattern are required" }, 400);
|
|
1907
|
+
}
|
|
1908
|
+
const { scheduleRecurring } = await import("./scheduler-CA5UNHZV.js");
|
|
1909
|
+
await scheduleRecurring(name, task || { type: "custom", name }, pattern);
|
|
1910
|
+
return c.json({ success: true }, 201);
|
|
1911
|
+
} catch (error) {
|
|
1912
|
+
return c.json({ error: "Failed to create job" }, 500);
|
|
1913
|
+
}
|
|
1914
|
+
});
|
|
1915
|
+
schedulerRouter.delete("/jobs/:key", async (c) => {
|
|
1916
|
+
try {
|
|
1917
|
+
const key = decodeURIComponent(c.req.param("key"));
|
|
1918
|
+
const { taskQueue } = await import("./scheduler-CA5UNHZV.js");
|
|
1919
|
+
await taskQueue.removeRepeatableByKey(key);
|
|
1920
|
+
return c.json({ success: true });
|
|
1921
|
+
} catch (error) {
|
|
1922
|
+
return c.json({ error: "Failed to remove job" }, 500);
|
|
1923
|
+
}
|
|
1924
|
+
});
|
|
1925
|
+
schedulerRouter.put("/jobs/:key", async (c) => {
|
|
1926
|
+
try {
|
|
1927
|
+
const key = decodeURIComponent(c.req.param("key"));
|
|
1928
|
+
const { name, pattern, task } = await c.req.json();
|
|
1929
|
+
if (!pattern) {
|
|
1930
|
+
return c.json({ error: "pattern is required" }, 400);
|
|
1931
|
+
}
|
|
1932
|
+
const { taskQueue, scheduleRecurring } = await import("./scheduler-CA5UNHZV.js");
|
|
1933
|
+
await taskQueue.removeRepeatableByKey(key);
|
|
1934
|
+
const jobName = name || key.split(":::")[0] || "custom-job";
|
|
1935
|
+
await scheduleRecurring(jobName, task || { type: "custom", name: jobName }, pattern);
|
|
1936
|
+
return c.json({ success: true });
|
|
1937
|
+
} catch (error) {
|
|
1938
|
+
return c.json({ error: "Failed to update job" }, 500);
|
|
1939
|
+
}
|
|
1940
|
+
});
|
|
1941
|
+
var scheduler_default = schedulerRouter;
|
|
1942
|
+
|
|
1943
|
+
// src/inputs/api/routes/alerts.ts
|
|
1944
|
+
import { Hono as Hono7 } from "hono";
|
|
1945
|
+
var alertsRouter = new Hono7();
|
|
1946
|
+
alertsRouter.get("/", async (c) => {
|
|
1947
|
+
try {
|
|
1948
|
+
const alerting = await import("./alerting-4I37GG4U.js");
|
|
1949
|
+
await alerting.loadAlertHistoryFromDb?.();
|
|
1950
|
+
const active = alerting.getActiveAlerts?.() ?? [];
|
|
1951
|
+
const history = alerting.getAlertHistory?.(50) ?? [];
|
|
1952
|
+
return c.json({ active, history });
|
|
1953
|
+
} catch {
|
|
1954
|
+
return c.json({ active: [], history: [] });
|
|
1955
|
+
}
|
|
1956
|
+
});
|
|
1957
|
+
alertsRouter.post("/:id/acknowledge", async (c) => {
|
|
1958
|
+
try {
|
|
1959
|
+
const id = c.req.param("id");
|
|
1960
|
+
const { by } = await c.req.json();
|
|
1961
|
+
const alerting = await import("./alerting-4I37GG4U.js");
|
|
1962
|
+
alerting.acknowledgeAlert?.(id, by || "web-user");
|
|
1963
|
+
return c.json({ success: true });
|
|
1964
|
+
} catch {
|
|
1965
|
+
return c.json({ error: "Failed to acknowledge alert" }, 500);
|
|
1966
|
+
}
|
|
1967
|
+
});
|
|
1968
|
+
alertsRouter.post("/:id/resolve", async (c) => {
|
|
1969
|
+
try {
|
|
1970
|
+
const id = c.req.param("id");
|
|
1971
|
+
const { by } = await c.req.json();
|
|
1972
|
+
const alerting = await import("./alerting-4I37GG4U.js");
|
|
1973
|
+
alerting.resolveAlert?.(id, by || "web-user");
|
|
1974
|
+
return c.json({ success: true });
|
|
1975
|
+
} catch {
|
|
1976
|
+
return c.json({ error: "Failed to resolve alert" }, 500);
|
|
1977
|
+
}
|
|
1978
|
+
});
|
|
1979
|
+
alertsRouter.get("/rules", async (c) => {
|
|
1980
|
+
try {
|
|
1981
|
+
const alerting = await import("./alerting-4I37GG4U.js");
|
|
1982
|
+
let rules = alerting.getAlertRules?.() ?? [];
|
|
1983
|
+
if (rules.length === 0 && alerting.initializeDefaultRules) {
|
|
1984
|
+
alerting.initializeDefaultRules();
|
|
1985
|
+
rules = alerting.getAlertRules?.() ?? [];
|
|
1986
|
+
}
|
|
1987
|
+
return c.json(rules);
|
|
1988
|
+
} catch {
|
|
1989
|
+
return c.json([]);
|
|
1990
|
+
}
|
|
1991
|
+
});
|
|
1992
|
+
alertsRouter.delete("/history", async (c) => {
|
|
1993
|
+
try {
|
|
1994
|
+
const alerting = await import("./alerting-4I37GG4U.js");
|
|
1995
|
+
alerting.clearAlertHistory?.();
|
|
1996
|
+
return c.json({ success: true });
|
|
1997
|
+
} catch {
|
|
1998
|
+
return c.json({ error: "Failed to clear alert history" }, 500);
|
|
1999
|
+
}
|
|
2000
|
+
});
|
|
2001
|
+
alertsRouter.post("/rules", async (c) => {
|
|
2002
|
+
try {
|
|
2003
|
+
const rule = await c.req.json();
|
|
2004
|
+
const alerting = await import("./alerting-4I37GG4U.js");
|
|
2005
|
+
alerting.addAlertRule?.(rule);
|
|
2006
|
+
return c.json({ success: true }, 201);
|
|
2007
|
+
} catch {
|
|
2008
|
+
return c.json({ error: "Failed to create rule" }, 500);
|
|
2009
|
+
}
|
|
2010
|
+
});
|
|
2011
|
+
alertsRouter.delete("/rules/:id", async (c) => {
|
|
2012
|
+
try {
|
|
2013
|
+
const id = c.req.param("id");
|
|
2014
|
+
const alerting = await import("./alerting-4I37GG4U.js");
|
|
2015
|
+
const removed = alerting.removeAlertRule?.(id);
|
|
2016
|
+
if (!removed) {
|
|
2017
|
+
return c.json({ error: "Rule not found" }, 404);
|
|
2018
|
+
}
|
|
2019
|
+
return c.json({ success: true });
|
|
2020
|
+
} catch {
|
|
2021
|
+
return c.json({ error: "Failed to delete rule" }, 500);
|
|
2022
|
+
}
|
|
2023
|
+
});
|
|
2024
|
+
alertsRouter.put("/rules/:id", async (c) => {
|
|
2025
|
+
try {
|
|
2026
|
+
const id = c.req.param("id");
|
|
2027
|
+
const updates = await c.req.json();
|
|
2028
|
+
const alerting = await import("./alerting-4I37GG4U.js");
|
|
2029
|
+
const rules = alerting.getAlertRules?.() ?? [];
|
|
2030
|
+
const existing = rules.find((r) => r.id === id);
|
|
2031
|
+
if (!existing) {
|
|
2032
|
+
return c.json({ error: "Rule not found" }, 404);
|
|
2033
|
+
}
|
|
2034
|
+
const updated = { ...existing, ...updates, id };
|
|
2035
|
+
alerting.addAlertRule?.(updated);
|
|
2036
|
+
return c.json({ success: true });
|
|
2037
|
+
} catch {
|
|
2038
|
+
return c.json({ error: "Failed to update rule" }, 500);
|
|
2039
|
+
}
|
|
2040
|
+
});
|
|
2041
|
+
var alerts_default = alertsRouter;
|
|
2042
|
+
|
|
2043
|
+
// src/inputs/api/routes/webhooks.ts
|
|
2044
|
+
import { Hono as Hono8 } from "hono";
|
|
2045
|
+
import { v4 as uuidv4 } from "uuid";
|
|
2046
|
+
var webhooksRouter = new Hono8();
|
|
2047
|
+
webhooksRouter.get("/", async (c) => {
|
|
2048
|
+
try {
|
|
2049
|
+
const { WorkflowStore } = await import("./workflow-store-ZYAYE5P6.js");
|
|
2050
|
+
const store = new WorkflowStore();
|
|
2051
|
+
const workflows = await store.getAllWorkflows();
|
|
2052
|
+
const results = workflows.map((w) => ({
|
|
2053
|
+
id: w.id,
|
|
2054
|
+
name: w.name,
|
|
2055
|
+
description: w.description || "",
|
|
2056
|
+
triggerType: Array.isArray(w.triggers) && w.triggers[0]?.type || "webhook",
|
|
2057
|
+
trigger: Array.isArray(w.triggers) && w.triggers[0] || {},
|
|
2058
|
+
actions: (Array.isArray(w.steps) ? w.steps : []).map((s) => ({
|
|
2059
|
+
type: s.action?.type || s.type || "unknown",
|
|
2060
|
+
name: s.action?.name || s.action?.type || s.type || "action"
|
|
2061
|
+
})),
|
|
2062
|
+
enabled: w.status === "active",
|
|
2063
|
+
createdAt: w.createdAt,
|
|
2064
|
+
updatedAt: w.updatedAt,
|
|
2065
|
+
lastTriggered: w.lastExecutedAt,
|
|
2066
|
+
executionCount: w.executionCount || 0
|
|
2067
|
+
}));
|
|
2068
|
+
return c.json(results);
|
|
2069
|
+
} catch {
|
|
2070
|
+
return c.json([]);
|
|
2071
|
+
}
|
|
2072
|
+
});
|
|
2073
|
+
webhooksRouter.post("/", async (c) => {
|
|
2074
|
+
try {
|
|
2075
|
+
const body = await c.req.json();
|
|
2076
|
+
if (!body.name) {
|
|
2077
|
+
return c.json({ error: "name is required" }, 400);
|
|
2078
|
+
}
|
|
2079
|
+
const { WorkflowStore } = await import("./workflow-store-ZYAYE5P6.js");
|
|
2080
|
+
const store = new WorkflowStore();
|
|
2081
|
+
const now = /* @__PURE__ */ new Date();
|
|
2082
|
+
const workflow = await store.createWorkflow({
|
|
2083
|
+
id: uuidv4(),
|
|
2084
|
+
name: body.name,
|
|
2085
|
+
description: body.description,
|
|
2086
|
+
status: "active",
|
|
2087
|
+
triggers: [{ type: body.triggerType || "webhook", config: body.trigger || {} }],
|
|
2088
|
+
steps: (body.actions || []).map((a, i) => ({
|
|
2089
|
+
id: uuidv4(),
|
|
2090
|
+
type: "action",
|
|
2091
|
+
action: { type: a.type, config: a.config || {} }
|
|
2092
|
+
})),
|
|
2093
|
+
executionCount: 0,
|
|
2094
|
+
createdAt: now,
|
|
2095
|
+
updatedAt: now
|
|
2096
|
+
});
|
|
2097
|
+
return c.json({ id: workflow.id, success: true }, 201);
|
|
2098
|
+
} catch (err) {
|
|
2099
|
+
return c.json({ error: err?.message || "Failed to create webhook" }, 500);
|
|
2100
|
+
}
|
|
2101
|
+
});
|
|
2102
|
+
webhooksRouter.put("/:id/toggle", async (c) => {
|
|
2103
|
+
try {
|
|
2104
|
+
const id = c.req.param("id");
|
|
2105
|
+
const { WorkflowStore } = await import("./workflow-store-ZYAYE5P6.js");
|
|
2106
|
+
const store = new WorkflowStore();
|
|
2107
|
+
const workflow = await store.getWorkflow(id);
|
|
2108
|
+
if (!workflow) return c.json({ error: "Workflow not found" }, 404);
|
|
2109
|
+
const newStatus = workflow.status === "active" ? "disabled" : "active";
|
|
2110
|
+
await store.updateWorkflow(id, { status: newStatus });
|
|
2111
|
+
return c.json({ success: true, enabled: newStatus === "active" });
|
|
2112
|
+
} catch {
|
|
2113
|
+
return c.json({ error: "Failed to toggle webhook" }, 500);
|
|
2114
|
+
}
|
|
2115
|
+
});
|
|
2116
|
+
webhooksRouter.delete("/:id", async (c) => {
|
|
2117
|
+
try {
|
|
2118
|
+
const id = c.req.param("id");
|
|
2119
|
+
const { WorkflowStore } = await import("./workflow-store-ZYAYE5P6.js");
|
|
2120
|
+
const store = new WorkflowStore();
|
|
2121
|
+
await store.deleteWorkflow(id);
|
|
2122
|
+
return c.json({ success: true });
|
|
2123
|
+
} catch {
|
|
2124
|
+
return c.json({ error: "Failed to delete webhook" }, 500);
|
|
2125
|
+
}
|
|
2126
|
+
});
|
|
2127
|
+
webhooksRouter.put("/:id", async (c) => {
|
|
2128
|
+
try {
|
|
2129
|
+
const id = c.req.param("id");
|
|
2130
|
+
const body = await c.req.json();
|
|
2131
|
+
const { WorkflowStore } = await import("./workflow-store-ZYAYE5P6.js");
|
|
2132
|
+
const store = new WorkflowStore();
|
|
2133
|
+
const workflow = await store.getWorkflow(id);
|
|
2134
|
+
if (!workflow) return c.json({ error: "Workflow not found" }, 404);
|
|
2135
|
+
const updates = {};
|
|
2136
|
+
if (body.name !== void 0) updates.name = body.name;
|
|
2137
|
+
if (body.description !== void 0) updates.description = body.description;
|
|
2138
|
+
if (body.triggerType !== void 0 || body.trigger !== void 0) {
|
|
2139
|
+
updates.triggers = [{ type: body.triggerType || "webhook", config: body.trigger || {} }];
|
|
2140
|
+
}
|
|
2141
|
+
if (body.actions !== void 0) {
|
|
2142
|
+
const { v4: uuidv4Gen } = await import("uuid");
|
|
2143
|
+
updates.steps = body.actions.map((a) => ({
|
|
2144
|
+
id: uuidv4Gen(),
|
|
2145
|
+
type: "action",
|
|
2146
|
+
action: { type: a.type, config: a.config || {} }
|
|
2147
|
+
}));
|
|
2148
|
+
}
|
|
2149
|
+
await store.updateWorkflow(id, updates);
|
|
2150
|
+
return c.json({ success: true });
|
|
2151
|
+
} catch {
|
|
2152
|
+
return c.json({ error: "Failed to update webhook" }, 500);
|
|
2153
|
+
}
|
|
2154
|
+
});
|
|
2155
|
+
webhooksRouter.post("/seed", async (c) => {
|
|
2156
|
+
try {
|
|
2157
|
+
const { WorkflowStore } = await import("./workflow-store-ZYAYE5P6.js");
|
|
2158
|
+
const store = new WorkflowStore();
|
|
2159
|
+
const now = /* @__PURE__ */ new Date();
|
|
2160
|
+
const defaults = [
|
|
2161
|
+
{
|
|
2162
|
+
name: "GitHub Push Notifications",
|
|
2163
|
+
description: "Alert on pushes to main branch via GitHub webhook",
|
|
2164
|
+
triggers: [{
|
|
2165
|
+
type: "webhook",
|
|
2166
|
+
config: {
|
|
2167
|
+
path: "/webhooks/github",
|
|
2168
|
+
methods: ["POST"],
|
|
2169
|
+
secret: "",
|
|
2170
|
+
filter: { type: "key_match", expression: "ref", expectedValue: "refs/heads/main" }
|
|
2171
|
+
}
|
|
2172
|
+
}],
|
|
2173
|
+
steps: [
|
|
2174
|
+
{ id: uuidv4(), type: "action", action: { type: "send_message", config: { channel: "web", message: "New push to main: {{trigger.data.head_commit.message}}" } } }
|
|
2175
|
+
]
|
|
2176
|
+
},
|
|
2177
|
+
{
|
|
2178
|
+
name: "Email-to-Chat Forward",
|
|
2179
|
+
description: "Forward important emails to web chat",
|
|
2180
|
+
triggers: [{
|
|
2181
|
+
type: "event",
|
|
2182
|
+
config: { source: "email", eventName: "email_received" }
|
|
2183
|
+
}],
|
|
2184
|
+
steps: [
|
|
2185
|
+
{ id: uuidv4(), type: "action", action: { type: "send_message", config: { channel: "web", message: "New email from {{trigger.data.from}}: {{trigger.data.subject}}" } } }
|
|
2186
|
+
]
|
|
2187
|
+
},
|
|
2188
|
+
{
|
|
2189
|
+
name: "Alert on High Error Rate",
|
|
2190
|
+
description: "Send notification when error rate spikes",
|
|
2191
|
+
triggers: [{
|
|
2192
|
+
type: "event",
|
|
2193
|
+
config: { source: "alerting", eventName: "alert_fired", filter: { severity: "error" } }
|
|
2194
|
+
}],
|
|
2195
|
+
steps: [
|
|
2196
|
+
{ id: uuidv4(), type: "action", action: { type: "send_message", config: { channel: "web", message: "ALERT: {{trigger.data.message}} (severity: {{trigger.data.severity}})" } } },
|
|
2197
|
+
{ id: uuidv4(), type: "action", action: { type: "log", config: { level: "error", message: "Alert fired: {{trigger.data.message}}" } } }
|
|
2198
|
+
]
|
|
2199
|
+
},
|
|
2200
|
+
{
|
|
2201
|
+
name: "Daily Summary Report",
|
|
2202
|
+
description: "Generate and send a daily activity summary at 9 AM",
|
|
2203
|
+
triggers: [{
|
|
2204
|
+
type: "time",
|
|
2205
|
+
config: { pattern: "0 9 * * *", timezone: "America/New_York" }
|
|
2206
|
+
}],
|
|
2207
|
+
steps: [
|
|
2208
|
+
{ id: uuidv4(), type: "action", action: { type: "run_tool", config: { tool: "get_time", input: {} } } },
|
|
2209
|
+
{ id: uuidv4(), type: "action", action: { type: "send_message", config: { channel: "web", message: "Good morning! Here's your daily summary for {{date}}." } } }
|
|
2210
|
+
]
|
|
2211
|
+
},
|
|
2212
|
+
{
|
|
2213
|
+
name: "External API Health Check",
|
|
2214
|
+
description: "Monitor external API endpoint every 10 minutes",
|
|
2215
|
+
triggers: [{
|
|
2216
|
+
type: "time",
|
|
2217
|
+
config: { pattern: "*/10 * * * *" }
|
|
2218
|
+
}],
|
|
2219
|
+
steps: [
|
|
2220
|
+
{ id: uuidv4(), type: "action", action: { type: "http_request", config: { method: "GET", url: "https://api.github.com/rate_limit", headers: {} } } },
|
|
2221
|
+
{ id: uuidv4(), type: "action", action: { type: "log", config: { level: "info", message: "Health check completed" } } }
|
|
2222
|
+
]
|
|
2223
|
+
}
|
|
2224
|
+
];
|
|
2225
|
+
let created = 0;
|
|
2226
|
+
const existing = await store.getAllWorkflows();
|
|
2227
|
+
const existingNames = new Set(existing.map((w) => w.name));
|
|
2228
|
+
for (const wf of defaults) {
|
|
2229
|
+
if (!existingNames.has(wf.name)) {
|
|
2230
|
+
await store.createWorkflow({
|
|
2231
|
+
id: uuidv4(),
|
|
2232
|
+
...wf,
|
|
2233
|
+
status: "active",
|
|
2234
|
+
executionCount: 0,
|
|
2235
|
+
createdAt: now,
|
|
2236
|
+
updatedAt: now
|
|
2237
|
+
});
|
|
2238
|
+
created++;
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
return c.json({ success: true, created, total: existing.length + created });
|
|
2242
|
+
} catch (err) {
|
|
2243
|
+
return c.json({ error: err?.message || "Failed to seed webhooks" }, 500);
|
|
2244
|
+
}
|
|
2245
|
+
});
|
|
2246
|
+
var webhooks_default = webhooksRouter;
|
|
2247
|
+
|
|
2248
|
+
// src/inputs/api/routes/github.ts
|
|
2249
|
+
import { Hono as Hono9 } from "hono";
|
|
2250
|
+
var githubRouter = new Hono9();
|
|
2251
|
+
githubRouter.get("/repos", async (c) => {
|
|
2252
|
+
try {
|
|
2253
|
+
const { env: env2 } = await import("./env-CHOFICED.js");
|
|
2254
|
+
if (!env2.GITHUB_TOKEN) {
|
|
2255
|
+
return c.json({ error: "GITHUB_TOKEN not configured" }, 400);
|
|
2256
|
+
}
|
|
2257
|
+
const github = await import("./github-KGNILDWJ.js");
|
|
2258
|
+
const repos = await github.listRepositories();
|
|
2259
|
+
return c.json(repos);
|
|
2260
|
+
} catch (error) {
|
|
2261
|
+
return c.json([], 200);
|
|
2262
|
+
}
|
|
2263
|
+
});
|
|
2264
|
+
githubRouter.get("/issues", async (c) => {
|
|
2265
|
+
try {
|
|
2266
|
+
const { env: env2 } = await import("./env-CHOFICED.js");
|
|
2267
|
+
if (!env2.GITHUB_TOKEN) {
|
|
2268
|
+
return c.json({ error: "GITHUB_TOKEN not configured" }, 400);
|
|
2269
|
+
}
|
|
2270
|
+
const repo = c.req.query("repo");
|
|
2271
|
+
const state = c.req.query("state") || "open";
|
|
2272
|
+
const github = await import("./github-KGNILDWJ.js");
|
|
2273
|
+
if (repo) {
|
|
2274
|
+
const [owner, name] = repo.split("/");
|
|
2275
|
+
const issues2 = await github.listIssues(owner, name, { state });
|
|
2276
|
+
return c.json(issues2);
|
|
2277
|
+
}
|
|
2278
|
+
const issues = await github.listIssues("", "", { state });
|
|
2279
|
+
return c.json(issues);
|
|
2280
|
+
} catch {
|
|
2281
|
+
return c.json([], 200);
|
|
2282
|
+
}
|
|
2283
|
+
});
|
|
2284
|
+
githubRouter.get("/prs", async (c) => {
|
|
2285
|
+
try {
|
|
2286
|
+
const { env: env2 } = await import("./env-CHOFICED.js");
|
|
2287
|
+
if (!env2.GITHUB_TOKEN) {
|
|
2288
|
+
return c.json({ error: "GITHUB_TOKEN not configured" }, 400);
|
|
2289
|
+
}
|
|
2290
|
+
const repo = c.req.query("repo");
|
|
2291
|
+
const state = c.req.query("state") || "open";
|
|
2292
|
+
const github = await import("./github-KGNILDWJ.js");
|
|
2293
|
+
if (repo) {
|
|
2294
|
+
const [owner, name] = repo.split("/");
|
|
2295
|
+
const prs2 = await github.listPullRequests(owner, name, { state });
|
|
2296
|
+
return c.json(prs2);
|
|
2297
|
+
}
|
|
2298
|
+
const prs = await github.listPullRequests("", "", { state });
|
|
2299
|
+
return c.json(prs);
|
|
2300
|
+
} catch {
|
|
2301
|
+
return c.json([], 200);
|
|
2302
|
+
}
|
|
2303
|
+
});
|
|
2304
|
+
var github_default = githubRouter;
|
|
2305
|
+
|
|
2306
|
+
// src/inputs/api/routes/metrics.ts
|
|
2307
|
+
import { Hono as Hono10 } from "hono";
|
|
2308
|
+
var metricsRouter = new Hono10();
|
|
2309
|
+
async function requireApiKey(c, next) {
|
|
2310
|
+
const apiKey = c.req.header("x-api-key");
|
|
2311
|
+
if (!apiKey) {
|
|
2312
|
+
return c.json({ error: "API key required" }, 401);
|
|
2313
|
+
}
|
|
2314
|
+
const rateLimitResult = await checkRateLimit(apiKey, "api/metrics");
|
|
2315
|
+
if (!rateLimitResult.allowed) {
|
|
2316
|
+
return c.json(
|
|
2317
|
+
{
|
|
2318
|
+
error: "Rate limit exceeded",
|
|
2319
|
+
retryAfter: rateLimitResult.retryAfterMs
|
|
2320
|
+
},
|
|
2321
|
+
429
|
|
2322
|
+
);
|
|
2323
|
+
}
|
|
2324
|
+
await next();
|
|
2325
|
+
}
|
|
2326
|
+
metricsRouter.use("*", requireApiKey);
|
|
2327
|
+
metricsRouter.get("/aggregation", async (c) => {
|
|
2328
|
+
const metricType = c.req.query("type");
|
|
2329
|
+
const startDate = c.req.query("start");
|
|
2330
|
+
const endDate = c.req.query("end");
|
|
2331
|
+
const groupBy = c.req.query("groupBy");
|
|
2332
|
+
if (!metricType) {
|
|
2333
|
+
return c.json({ error: "Metric type required" }, 400);
|
|
2334
|
+
}
|
|
2335
|
+
const start = startDate ? new Date(startDate) : new Date(Date.now() - 24 * 60 * 60 * 1e3);
|
|
2336
|
+
const end = endDate ? new Date(endDate) : /* @__PURE__ */ new Date();
|
|
2337
|
+
const aggregation = await getMetricAggregates(metricType, start, end);
|
|
2338
|
+
return c.json({
|
|
2339
|
+
type: metricType,
|
|
2340
|
+
period: { start: start.toISOString(), end: end.toISOString() },
|
|
2341
|
+
aggregation
|
|
2342
|
+
});
|
|
2343
|
+
});
|
|
2344
|
+
metricsRouter.get("/timeseries", async (c) => {
|
|
2345
|
+
const metricType = c.req.query("type");
|
|
2346
|
+
const startDate = c.req.query("start");
|
|
2347
|
+
const endDate = c.req.query("end");
|
|
2348
|
+
const interval = c.req.query("interval");
|
|
2349
|
+
if (!metricType) {
|
|
2350
|
+
return c.json({ error: "Metric type required" }, 400);
|
|
2351
|
+
}
|
|
2352
|
+
const start = startDate ? new Date(startDate) : new Date(Date.now() - 24 * 60 * 60 * 1e3);
|
|
2353
|
+
const end = endDate ? new Date(endDate) : /* @__PURE__ */ new Date();
|
|
2354
|
+
const timeSeries = await getMetricTimeSeries(metricType, start, end);
|
|
2355
|
+
return c.json({
|
|
2356
|
+
type: metricType,
|
|
2357
|
+
period: { start: start.toISOString(), end: end.toISOString() },
|
|
2358
|
+
interval: interval || "hour",
|
|
2359
|
+
data: timeSeries
|
|
2360
|
+
});
|
|
2361
|
+
});
|
|
2362
|
+
metricsRouter.post("/record", async (c) => {
|
|
2363
|
+
const body = await c.req.json();
|
|
2364
|
+
const { type, value, metadata, userId } = body;
|
|
2365
|
+
if (!type || value === void 0) {
|
|
2366
|
+
return c.json({ error: "Type and value required" }, 400);
|
|
2367
|
+
}
|
|
2368
|
+
await recordMetric({
|
|
2369
|
+
name: type,
|
|
2370
|
+
value,
|
|
2371
|
+
tags: metadata
|
|
2372
|
+
});
|
|
2373
|
+
return c.json({ success: true });
|
|
2374
|
+
});
|
|
2375
|
+
metricsRouter.post("/flush", async (c) => {
|
|
2376
|
+
await flushMetrics();
|
|
2377
|
+
return c.json({ success: true, message: "Metrics flushed" });
|
|
2378
|
+
});
|
|
2379
|
+
metricsRouter.get("/errors/stats", async (c) => {
|
|
2380
|
+
const days = parseInt(c.req.query("days") || "7");
|
|
2381
|
+
const now = /* @__PURE__ */ new Date();
|
|
2382
|
+
const since = new Date(now.getTime() - days * 24 * 60 * 60 * 1e3);
|
|
2383
|
+
const stats = await getErrorStats(since, now);
|
|
2384
|
+
return c.json({
|
|
2385
|
+
period: `Last ${days} days`,
|
|
2386
|
+
stats
|
|
2387
|
+
});
|
|
2388
|
+
});
|
|
2389
|
+
metricsRouter.get("/errors/recent", async (c) => {
|
|
2390
|
+
const limit = parseInt(c.req.query("limit") || "20");
|
|
2391
|
+
const category = c.req.query("category");
|
|
2392
|
+
const resolved = c.req.query("resolved");
|
|
2393
|
+
const errors = await getRecentErrors(
|
|
2394
|
+
limit,
|
|
2395
|
+
category
|
|
2396
|
+
);
|
|
2397
|
+
return c.json({
|
|
2398
|
+
count: errors.length,
|
|
2399
|
+
errors
|
|
2400
|
+
});
|
|
2401
|
+
});
|
|
2402
|
+
metricsRouter.get("/health", async (c) => {
|
|
2403
|
+
const now = /* @__PURE__ */ new Date();
|
|
2404
|
+
const recentLatency = await getMetricAggregates(
|
|
2405
|
+
"response_latency",
|
|
2406
|
+
new Date(now.getTime() - 5 * 60 * 1e3),
|
|
2407
|
+
now
|
|
2408
|
+
);
|
|
2409
|
+
const recentErrors = await getRecentErrors(10);
|
|
2410
|
+
const status = {
|
|
2411
|
+
healthy: true,
|
|
2412
|
+
timestamp: now.toISOString(),
|
|
2413
|
+
metrics: {
|
|
2414
|
+
avgLatencyMs: recentLatency.avg || 0,
|
|
2415
|
+
recentErrorCount: recentErrors.length
|
|
2416
|
+
}
|
|
2417
|
+
};
|
|
2418
|
+
if (recentErrors.length > 5) {
|
|
2419
|
+
status.healthy = false;
|
|
2420
|
+
}
|
|
2421
|
+
return c.json(status, status.healthy ? 200 : 503);
|
|
2422
|
+
});
|
|
2423
|
+
metricsRouter.get("/overview", async (c) => {
|
|
2424
|
+
const now = /* @__PURE__ */ new Date();
|
|
2425
|
+
const dayAgo = new Date(now.getTime() - 24 * 60 * 60 * 1e3);
|
|
2426
|
+
const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1e3);
|
|
2427
|
+
const [
|
|
2428
|
+
latencyDay,
|
|
2429
|
+
tokensDay,
|
|
2430
|
+
errorsDay,
|
|
2431
|
+
latencyWeek,
|
|
2432
|
+
tokensWeek,
|
|
2433
|
+
errorsWeek
|
|
2434
|
+
] = await Promise.all([
|
|
2435
|
+
getMetricAggregates("response_latency", dayAgo, now),
|
|
2436
|
+
getMetricAggregates("token_usage_input", dayAgo, now),
|
|
2437
|
+
getErrorStats(dayAgo, now),
|
|
2438
|
+
getMetricAggregates("response_latency", weekAgo, now),
|
|
2439
|
+
getMetricAggregates("token_usage_input", weekAgo, now),
|
|
2440
|
+
getErrorStats(weekAgo, now)
|
|
2441
|
+
]);
|
|
2442
|
+
return c.json({
|
|
2443
|
+
timestamp: now.toISOString(),
|
|
2444
|
+
last24Hours: {
|
|
2445
|
+
avgLatencyMs: latencyDay.avg || 0,
|
|
2446
|
+
totalTokens: tokensDay.sum || 0,
|
|
2447
|
+
requestCount: latencyDay.count || 0,
|
|
2448
|
+
errorCount: errorsDay.total || 0
|
|
2449
|
+
},
|
|
2450
|
+
last7Days: {
|
|
2451
|
+
avgLatencyMs: latencyWeek.avg || 0,
|
|
2452
|
+
totalTokens: tokensWeek.sum || 0,
|
|
2453
|
+
requestCount: latencyWeek.count || 0,
|
|
2454
|
+
errorCount: errorsWeek.total || 0
|
|
2455
|
+
}
|
|
2456
|
+
});
|
|
2457
|
+
});
|
|
2458
|
+
var metrics_default = metricsRouter;
|
|
2459
|
+
|
|
2460
|
+
// src/inputs/api/routes/mcp.ts
|
|
2461
|
+
import { Hono as Hono11 } from "hono";
|
|
2462
|
+
var mcpRouter = new Hono11();
|
|
2463
|
+
mcpRouter.get("/servers", async (c) => {
|
|
2464
|
+
try {
|
|
2465
|
+
const { getMCPRegistry } = await import("./tools-FGPN522P.js");
|
|
2466
|
+
const registry = getMCPRegistry();
|
|
2467
|
+
if (!registry) {
|
|
2468
|
+
return c.json({ enabled: false, servers: [], connectedCount: 0, totalToolCount: 0 });
|
|
2469
|
+
}
|
|
2470
|
+
const states = registry.getServerStates();
|
|
2471
|
+
const servers = states.map((s) => ({
|
|
2472
|
+
id: s.config?.id || "unknown",
|
|
2473
|
+
name: s.config?.name || s.serverInfo?.name || s.config?.id || "Unknown",
|
|
2474
|
+
transport: s.config?.transport || "stdio",
|
|
2475
|
+
enabled: s.config?.enabled ?? true,
|
|
2476
|
+
status: s.status || "disconnected",
|
|
2477
|
+
serverVersion: s.serverInfo?.version || null,
|
|
2478
|
+
toolCount: Array.isArray(s.tools) ? s.tools.length : 0,
|
|
2479
|
+
tools: Array.isArray(s.tools) ? s.tools.map((t) => ({
|
|
2480
|
+
name: t.name,
|
|
2481
|
+
description: t.description || ""
|
|
2482
|
+
})) : [],
|
|
2483
|
+
lastError: s.lastError || null,
|
|
2484
|
+
lastActivity: s.lastActivity || null,
|
|
2485
|
+
command: s.config?.command || null,
|
|
2486
|
+
args: s.config?.args || []
|
|
2487
|
+
}));
|
|
2488
|
+
return c.json({
|
|
2489
|
+
enabled: true,
|
|
2490
|
+
connectedCount: registry.connectedCount,
|
|
2491
|
+
totalToolCount: registry.totalToolCount,
|
|
2492
|
+
servers
|
|
2493
|
+
});
|
|
2494
|
+
} catch {
|
|
2495
|
+
return c.json({ enabled: false, servers: [], connectedCount: 0, totalToolCount: 0 });
|
|
2496
|
+
}
|
|
2497
|
+
});
|
|
2498
|
+
mcpRouter.post("/servers/:id/refresh", async (c) => {
|
|
2499
|
+
try {
|
|
2500
|
+
const { getMCPRegistry } = await import("./tools-FGPN522P.js");
|
|
2501
|
+
const registry = getMCPRegistry();
|
|
2502
|
+
if (!registry) return c.json({ error: "MCP not initialized" }, 500);
|
|
2503
|
+
await registry.refreshAllTools();
|
|
2504
|
+
return c.json({ success: true });
|
|
2505
|
+
} catch (err) {
|
|
2506
|
+
return c.json({ error: err?.message || "Failed to refresh" }, 500);
|
|
2507
|
+
}
|
|
2508
|
+
});
|
|
2509
|
+
var mcp_default = mcpRouter;
|
|
2510
|
+
|
|
2511
|
+
// src/inputs/api/routes/bots.ts
|
|
2512
|
+
import { Hono as Hono12 } from "hono";
|
|
2513
|
+
import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
2514
|
+
import { resolve } from "path";
|
|
2515
|
+
var botsRouter = new Hono12();
|
|
2516
|
+
var BOT_FIELDS = {
|
|
2517
|
+
telegram: [
|
|
2518
|
+
{ key: "botToken", envVar: "TELEGRAM_BOT_TOKEN", label: "Bot Token", type: "password", placeholder: "123456:ABC-DEF1234...", help: "Get from @BotFather on Telegram", required: true },
|
|
2519
|
+
{ key: "chatId", envVar: "TELEGRAM_CHAT_ID", label: "Chat ID", type: "text", placeholder: "-1001234567890", help: "Your chat/group ID for notifications" }
|
|
2520
|
+
],
|
|
2521
|
+
discord: [
|
|
2522
|
+
{ key: "botToken", envVar: "DISCORD_BOT_TOKEN", label: "Bot Token", type: "password", placeholder: "MTIz...", help: "From Discord Developer Portal > Bot", required: true },
|
|
2523
|
+
{ key: "clientId", envVar: "DISCORD_CLIENT_ID", label: "Client ID", type: "text", placeholder: "123456789012345678", help: "Application ID from Discord Developer Portal" },
|
|
2524
|
+
{ key: "guildId", envVar: "DISCORD_GUILD_ID", label: "Guild (Server) ID", type: "text", placeholder: "123456789012345678", help: "Right-click server > Copy Server ID" },
|
|
2525
|
+
{ key: "allowedUserIds", envVar: "DISCORD_ALLOWED_USER_IDS", label: "Allowed User IDs", type: "text", placeholder: "id1,id2,id3", help: "Comma-separated Discord user IDs" },
|
|
2526
|
+
{ key: "allowedRoleIds", envVar: "DISCORD_ALLOWED_ROLE_IDS", label: "Allowed Role IDs", type: "text", placeholder: "id1,id2", help: "Comma-separated Discord role IDs" }
|
|
2527
|
+
],
|
|
2528
|
+
slack: [
|
|
2529
|
+
{ key: "botToken", envVar: "SLACK_BOT_TOKEN", label: "Bot Token", type: "password", placeholder: "xoxb-...", help: "From Slack App > OAuth & Permissions", required: true },
|
|
2530
|
+
{ key: "signingSecret", envVar: "SLACK_SIGNING_SECRET", label: "Signing Secret", type: "password", placeholder: "abc123...", help: "From Slack App > Basic Information", required: true },
|
|
2531
|
+
{ key: "appToken", envVar: "SLACK_APP_TOKEN", label: "App Token", type: "password", placeholder: "xapp-...", help: "For Socket Mode \u2014 from Slack App > Basic Information" },
|
|
2532
|
+
{ key: "socketMode", envVar: "SLACK_SOCKET_MODE", label: "Socket Mode", type: "boolean", help: "Use WebSocket instead of HTTP events" },
|
|
2533
|
+
{ key: "port", envVar: "SLACK_PORT", label: "Port", type: "number", placeholder: "3000", help: "HTTP listener port (if not using Socket Mode)" },
|
|
2534
|
+
{ key: "allowedUserIds", envVar: "SLACK_ALLOWED_USER_IDS", label: "Allowed User IDs", type: "text", placeholder: "U01ABC,U02DEF", help: "Comma-separated Slack user IDs" },
|
|
2535
|
+
{ key: "allowedChannelIds", envVar: "SLACK_ALLOWED_CHANNEL_IDS", label: "Allowed Channel IDs", type: "text", placeholder: "C01ABC,C02DEF", help: "Comma-separated Slack channel IDs" }
|
|
2536
|
+
],
|
|
2537
|
+
whatsapp: [
|
|
2538
|
+
{ key: "enabled", envVar: "WHATSAPP_ENABLED", label: "Enabled", type: "boolean", help: "Enable WhatsApp bot", required: true },
|
|
2539
|
+
{ key: "authDir", envVar: "WHATSAPP_AUTH_DIR", label: "Auth Directory", type: "text", placeholder: "./whatsapp-auth", help: "Directory for session storage" },
|
|
2540
|
+
{ key: "allowedNumbers", envVar: "WHATSAPP_ALLOWED_NUMBERS", label: "Allowed Numbers", type: "text", placeholder: "+1234567890,+0987654321", help: "Comma-separated phone numbers" }
|
|
2541
|
+
],
|
|
2542
|
+
signal: [
|
|
2543
|
+
{ key: "enabled", envVar: "SIGNAL_ENABLED", label: "Enabled", type: "boolean", help: "Enable Signal bot", required: true },
|
|
2544
|
+
{ key: "phoneNumber", envVar: "SIGNAL_PHONE_NUMBER", label: "Phone Number", type: "text", placeholder: "+1234567890", help: "Registered Signal phone number", required: true },
|
|
2545
|
+
{ key: "cliPath", envVar: "SIGNAL_CLI_PATH", label: "CLI Path", type: "text", placeholder: "signal-cli", help: "Path to signal-cli binary" },
|
|
2546
|
+
{ key: "allowedNumbers", envVar: "SIGNAL_ALLOWED_NUMBERS", label: "Allowed Numbers", type: "text", placeholder: "+1234567890,+0987654321", help: "Comma-separated phone numbers" }
|
|
2547
|
+
],
|
|
2548
|
+
imessage: [
|
|
2549
|
+
{ key: "enabled", envVar: "IMESSAGE_ENABLED", label: "Enabled", type: "boolean", help: "Enable iMessage bot (macOS only)", required: true },
|
|
2550
|
+
{ key: "mode", envVar: "IMESSAGE_MODE", label: "Mode", type: "select", options: ["applescript", "bluebubbles"], help: "AppleScript (local) or BlueBubbles (remote)" },
|
|
2551
|
+
{ key: "blueBubblesUrl", envVar: "IMESSAGE_BLUEBUBBLES_URL", label: "BlueBubbles URL", type: "text", placeholder: "http://localhost:1234", help: "BlueBubbles server URL" },
|
|
2552
|
+
{ key: "blueBubblesPassword", envVar: "IMESSAGE_BLUEBUBBLES_PASSWORD", label: "BlueBubbles Password", type: "password", help: "BlueBubbles server password" },
|
|
2553
|
+
{ key: "allowedNumbers", envVar: "IMESSAGE_ALLOWED_NUMBERS", label: "Allowed Numbers", type: "text", placeholder: "+1234567890,+0987654321", help: "Comma-separated phone numbers" }
|
|
2554
|
+
],
|
|
2555
|
+
matrix: [
|
|
2556
|
+
{ key: "enabled", envVar: "MATRIX_ENABLED", label: "Enabled", type: "boolean", help: "Enable Matrix bot", required: true },
|
|
2557
|
+
{ key: "homeserverUrl", envVar: "MATRIX_HOMESERVER_URL", label: "Homeserver URL", type: "text", placeholder: "https://matrix.org", help: "Matrix homeserver URL", required: true },
|
|
2558
|
+
{ key: "accessToken", envVar: "MATRIX_ACCESS_TOKEN", label: "Access Token", type: "password", help: "Matrix access token", required: true },
|
|
2559
|
+
{ key: "userId", envVar: "MATRIX_USER_ID", label: "User ID", type: "text", placeholder: "@bot:matrix.org", help: "Bot's Matrix user ID" },
|
|
2560
|
+
{ key: "autoJoin", envVar: "MATRIX_AUTO_JOIN", label: "Auto Join Rooms", type: "boolean", help: "Automatically join rooms when invited" },
|
|
2561
|
+
{ key: "e2eEnabled", envVar: "MATRIX_E2E_ENABLED", label: "E2E Encryption", type: "boolean", help: "Enable end-to-end encryption" },
|
|
2562
|
+
{ key: "allowedRoomIds", envVar: "MATRIX_ALLOWED_ROOM_IDS", label: "Allowed Room IDs", type: "text", placeholder: "!roomid:matrix.org", help: "Comma-separated room IDs" }
|
|
2563
|
+
]
|
|
2564
|
+
};
|
|
2565
|
+
function getEnvFilePath() {
|
|
2566
|
+
return resolve(process.cwd(), ".env");
|
|
2567
|
+
}
|
|
2568
|
+
function parseEnvFile() {
|
|
2569
|
+
const envPath = getEnvFilePath();
|
|
2570
|
+
const vars = /* @__PURE__ */ new Map();
|
|
2571
|
+
if (!existsSync(envPath)) return vars;
|
|
2572
|
+
const content = readFileSync(envPath, "utf-8");
|
|
2573
|
+
for (const line of content.split("\n")) {
|
|
2574
|
+
const trimmed = line.trim();
|
|
2575
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
2576
|
+
const eqIdx = trimmed.indexOf("=");
|
|
2577
|
+
if (eqIdx === -1) continue;
|
|
2578
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
2579
|
+
let val = trimmed.slice(eqIdx + 1).trim();
|
|
2580
|
+
if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
|
|
2581
|
+
val = val.slice(1, -1);
|
|
2582
|
+
}
|
|
2583
|
+
vars.set(key, val);
|
|
2584
|
+
}
|
|
2585
|
+
return vars;
|
|
2586
|
+
}
|
|
2587
|
+
function updateEnvFile(updates) {
|
|
2588
|
+
const envPath = getEnvFilePath();
|
|
2589
|
+
let content = existsSync(envPath) ? readFileSync(envPath, "utf-8") : "";
|
|
2590
|
+
const lines = content.split("\n");
|
|
2591
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
2592
|
+
const lineIdx = lines.findIndex((l) => {
|
|
2593
|
+
const trimmed = l.trim();
|
|
2594
|
+
return !trimmed.startsWith("#") && trimmed.startsWith(key + "=");
|
|
2595
|
+
});
|
|
2596
|
+
const newLine = `${key}=${value}`;
|
|
2597
|
+
if (lineIdx >= 0) {
|
|
2598
|
+
lines[lineIdx] = newLine;
|
|
2599
|
+
} else {
|
|
2600
|
+
lines.push(newLine);
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
writeFileSync(envPath, lines.join("\n"), "utf-8");
|
|
2604
|
+
}
|
|
2605
|
+
botsRouter.get("/status", async (c) => {
|
|
2606
|
+
try {
|
|
2607
|
+
const { env: env2 } = await import("./env-CHOFICED.js");
|
|
2608
|
+
const bots = [
|
|
2609
|
+
{
|
|
2610
|
+
id: "telegram",
|
|
2611
|
+
name: "Telegram",
|
|
2612
|
+
icon: "telegram",
|
|
2613
|
+
enabled: !!env2.TELEGRAM_BOT_TOKEN,
|
|
2614
|
+
config: {
|
|
2615
|
+
chatId: env2.TELEGRAM_CHAT_ID ? true : false
|
|
2616
|
+
},
|
|
2617
|
+
description: "Telegram bot via grammY framework"
|
|
2618
|
+
},
|
|
2619
|
+
{
|
|
2620
|
+
id: "discord",
|
|
2621
|
+
name: "Discord",
|
|
2622
|
+
icon: "discord",
|
|
2623
|
+
enabled: !!env2.DISCORD_BOT_TOKEN,
|
|
2624
|
+
config: {
|
|
2625
|
+
guildId: env2.DISCORD_GUILD_ID || null,
|
|
2626
|
+
clientId: env2.DISCORD_CLIENT_ID ? true : false,
|
|
2627
|
+
allowedUsers: env2.DISCORD_ALLOWED_USER_IDS ? env2.DISCORD_ALLOWED_USER_IDS.split(",").length : 0
|
|
2628
|
+
},
|
|
2629
|
+
description: "Discord bot via discord.js with slash commands and voice"
|
|
2630
|
+
},
|
|
2631
|
+
{
|
|
2632
|
+
id: "slack",
|
|
2633
|
+
name: "Slack",
|
|
2634
|
+
icon: "slack",
|
|
2635
|
+
enabled: !!env2.SLACK_BOT_TOKEN && !!env2.SLACK_SIGNING_SECRET,
|
|
2636
|
+
config: {
|
|
2637
|
+
socketMode: env2.SLACK_SOCKET_MODE || false,
|
|
2638
|
+
port: env2.SLACK_PORT || 3e3,
|
|
2639
|
+
allowedUsers: env2.SLACK_ALLOWED_USER_IDS ? env2.SLACK_ALLOWED_USER_IDS.split(",").length : 0,
|
|
2640
|
+
allowedChannels: env2.SLACK_ALLOWED_CHANNEL_IDS ? env2.SLACK_ALLOWED_CHANNEL_IDS.split(",").length : 0
|
|
2641
|
+
},
|
|
2642
|
+
description: "Slack bot via @slack/bolt with Socket Mode support"
|
|
2643
|
+
},
|
|
2644
|
+
{
|
|
2645
|
+
id: "whatsapp",
|
|
2646
|
+
name: "WhatsApp",
|
|
2647
|
+
icon: "whatsapp",
|
|
2648
|
+
enabled: !!env2.WHATSAPP_ENABLED,
|
|
2649
|
+
config: {
|
|
2650
|
+
allowedNumbers: env2.WHATSAPP_ALLOWED_NUMBERS ? env2.WHATSAPP_ALLOWED_NUMBERS.split(",").length : 0
|
|
2651
|
+
},
|
|
2652
|
+
description: "WhatsApp bot via Baileys with QR code authentication"
|
|
2653
|
+
},
|
|
2654
|
+
{
|
|
2655
|
+
id: "signal",
|
|
2656
|
+
name: "Signal",
|
|
2657
|
+
icon: "signal",
|
|
2658
|
+
enabled: !!env2.SIGNAL_ENABLED && !!env2.SIGNAL_PHONE_NUMBER,
|
|
2659
|
+
config: {
|
|
2660
|
+
hasPhoneNumber: !!env2.SIGNAL_PHONE_NUMBER,
|
|
2661
|
+
allowedNumbers: env2.SIGNAL_ALLOWED_NUMBERS ? env2.SIGNAL_ALLOWED_NUMBERS.split(",").length : 0
|
|
2662
|
+
},
|
|
2663
|
+
description: "Signal bot via signal-cli with end-to-end encryption"
|
|
2664
|
+
},
|
|
2665
|
+
{
|
|
2666
|
+
id: "imessage",
|
|
2667
|
+
name: "iMessage",
|
|
2668
|
+
icon: "imessage",
|
|
2669
|
+
enabled: !!env2.IMESSAGE_ENABLED,
|
|
2670
|
+
config: {
|
|
2671
|
+
mode: env2.IMESSAGE_MODE || "applescript",
|
|
2672
|
+
allowedNumbers: env2.IMESSAGE_ALLOWED_NUMBERS ? env2.IMESSAGE_ALLOWED_NUMBERS.split(",").length : 0
|
|
2673
|
+
},
|
|
2674
|
+
description: "iMessage bot via AppleScript or BlueBubbles API"
|
|
2675
|
+
},
|
|
2676
|
+
{
|
|
2677
|
+
id: "matrix",
|
|
2678
|
+
name: "Matrix",
|
|
2679
|
+
icon: "matrix",
|
|
2680
|
+
enabled: !!env2.MATRIX_ENABLED && !!env2.MATRIX_HOMESERVER_URL && !!env2.MATRIX_ACCESS_TOKEN,
|
|
2681
|
+
config: {
|
|
2682
|
+
homeserver: env2.MATRIX_HOMESERVER_URL || null,
|
|
2683
|
+
userId: env2.MATRIX_USER_ID || null,
|
|
2684
|
+
autoJoin: env2.MATRIX_AUTO_JOIN ?? true,
|
|
2685
|
+
e2eEnabled: env2.MATRIX_E2E_ENABLED || false,
|
|
2686
|
+
allowedRooms: env2.MATRIX_ALLOWED_ROOM_IDS ? env2.MATRIX_ALLOWED_ROOM_IDS.split(",").length : 0
|
|
2687
|
+
},
|
|
2688
|
+
description: "Matrix bot with optional end-to-end encryption"
|
|
2689
|
+
}
|
|
2690
|
+
];
|
|
2691
|
+
const enabledCount = bots.filter((b) => b.enabled).length;
|
|
2692
|
+
return c.json({ bots, enabledCount, totalCount: bots.length });
|
|
2693
|
+
} catch {
|
|
2694
|
+
return c.json({ bots: [], enabledCount: 0, totalCount: 0 });
|
|
2695
|
+
}
|
|
2696
|
+
});
|
|
2697
|
+
botsRouter.get("/fields", (c) => {
|
|
2698
|
+
return c.json(BOT_FIELDS);
|
|
2699
|
+
});
|
|
2700
|
+
botsRouter.get("/:id/config", async (c) => {
|
|
2701
|
+
const botId = c.req.param("id");
|
|
2702
|
+
const fields = BOT_FIELDS[botId];
|
|
2703
|
+
if (!fields) return c.json({ error: "Unknown bot" }, 404);
|
|
2704
|
+
const envVars = parseEnvFile();
|
|
2705
|
+
const values = {};
|
|
2706
|
+
for (const field of fields) {
|
|
2707
|
+
const raw = envVars.get(field.envVar) || "";
|
|
2708
|
+
if (field.type === "password" && raw) {
|
|
2709
|
+
values[field.key] = raw.length > 10 ? raw.slice(0, 4) + "..." + raw.slice(-4) : "****";
|
|
2710
|
+
} else {
|
|
2711
|
+
values[field.key] = raw;
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
return c.json({ botId, values, fields });
|
|
2715
|
+
});
|
|
2716
|
+
botsRouter.put("/:id/config", async (c) => {
|
|
2717
|
+
const botId = c.req.param("id");
|
|
2718
|
+
const fields = BOT_FIELDS[botId];
|
|
2719
|
+
if (!fields) return c.json({ error: "Unknown bot" }, 404);
|
|
2720
|
+
try {
|
|
2721
|
+
const body = await c.req.json();
|
|
2722
|
+
const updates = {};
|
|
2723
|
+
for (const field of fields) {
|
|
2724
|
+
if (field.key in body) {
|
|
2725
|
+
let val = body[field.key];
|
|
2726
|
+
if (field.type === "password" && (val.includes("...") || val === "****")) continue;
|
|
2727
|
+
updates[field.envVar] = val;
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
if (Object.keys(updates).length > 0) {
|
|
2731
|
+
updateEnvFile(updates);
|
|
2732
|
+
}
|
|
2733
|
+
return c.json({ success: true, updated: Object.keys(updates).length, note: "Restart required for changes to take effect" });
|
|
2734
|
+
} catch (err) {
|
|
2735
|
+
return c.json({ error: err?.message || "Failed to update config" }, 500);
|
|
2736
|
+
}
|
|
2737
|
+
});
|
|
2738
|
+
var bots_default = botsRouter;
|
|
2739
|
+
|
|
2740
|
+
// src/inputs/api/routes/users.ts
|
|
2741
|
+
import { Hono as Hono13 } from "hono";
|
|
2742
|
+
var usersRouter = new Hono13();
|
|
2743
|
+
usersRouter.get("/", async (c) => {
|
|
2744
|
+
try {
|
|
2745
|
+
const { searchUsers } = await import("./multi-user-S56GUD6L.js");
|
|
2746
|
+
const users2 = await searchUsers({});
|
|
2747
|
+
return c.json(users2);
|
|
2748
|
+
} catch {
|
|
2749
|
+
return c.json([]);
|
|
2750
|
+
}
|
|
2751
|
+
});
|
|
2752
|
+
usersRouter.post("/", async (c) => {
|
|
2753
|
+
try {
|
|
2754
|
+
const { email: email2, name, role } = await c.req.json();
|
|
2755
|
+
if (!email2) {
|
|
2756
|
+
return c.json({ error: "email is required" }, 400);
|
|
2757
|
+
}
|
|
2758
|
+
const { createUser } = await import("./multi-user-S56GUD6L.js");
|
|
2759
|
+
const user = await createUser({
|
|
2760
|
+
email: email2,
|
|
2761
|
+
name: name || "",
|
|
2762
|
+
role: role || "user"
|
|
2763
|
+
});
|
|
2764
|
+
return c.json(user, 201);
|
|
2765
|
+
} catch (error) {
|
|
2766
|
+
return c.json({ error: "Failed to create user" }, 500);
|
|
2767
|
+
}
|
|
2768
|
+
});
|
|
2769
|
+
usersRouter.put("/:id", async (c) => {
|
|
2770
|
+
try {
|
|
2771
|
+
const id = c.req.param("id");
|
|
2772
|
+
const updates = await c.req.json();
|
|
2773
|
+
if (updates.status === "suspended") {
|
|
2774
|
+
const { suspendUser } = await import("./multi-user-S56GUD6L.js");
|
|
2775
|
+
await suspendUser(id, "Suspended via dashboard");
|
|
2776
|
+
return c.json({ success: true });
|
|
2777
|
+
}
|
|
2778
|
+
if (updates.status === "active") {
|
|
2779
|
+
const { reactivateUser } = await import("./multi-user-S56GUD6L.js");
|
|
2780
|
+
await reactivateUser(id);
|
|
2781
|
+
return c.json({ success: true });
|
|
2782
|
+
}
|
|
2783
|
+
const { updateUser } = await import("./multi-user-S56GUD6L.js");
|
|
2784
|
+
await updateUser(id, updates);
|
|
2785
|
+
return c.json({ success: true });
|
|
2786
|
+
} catch (error) {
|
|
2787
|
+
return c.json({ error: "Failed to update user" }, 500);
|
|
2788
|
+
}
|
|
2789
|
+
});
|
|
2790
|
+
usersRouter.delete("/:id", async (c) => {
|
|
2791
|
+
try {
|
|
2792
|
+
const id = c.req.param("id");
|
|
2793
|
+
const { deleteUser } = await import("./multi-user-S56GUD6L.js");
|
|
2794
|
+
await deleteUser(id);
|
|
2795
|
+
return c.json({ success: true });
|
|
2796
|
+
} catch (error) {
|
|
2797
|
+
return c.json({ error: "Failed to delete user" }, 500);
|
|
2798
|
+
}
|
|
2799
|
+
});
|
|
2800
|
+
var users_default = usersRouter;
|
|
2801
|
+
|
|
2802
|
+
// src/inputs/api/server.ts
|
|
2803
|
+
import { tmpdir } from "os";
|
|
2804
|
+
import { resolve as resolve2, join } from "path";
|
|
2805
|
+
import { randomBytes as randomBytes3 } from "crypto";
|
|
2806
|
+
var serveStatic;
|
|
2807
|
+
try {
|
|
2808
|
+
serveStatic = __require("hono/bun").serveStatic;
|
|
2809
|
+
} catch {
|
|
2810
|
+
serveStatic = () => async (_c, next) => next();
|
|
2811
|
+
}
|
|
2812
|
+
var app = new Hono14();
|
|
2813
|
+
app.use("*", logger());
|
|
2814
|
+
app.use("/api/*", cors());
|
|
2815
|
+
app.use("/api/*", authMiddleware());
|
|
2816
|
+
app.get("/health", (c) => {
|
|
2817
|
+
return c.json({ status: "ok", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
2818
|
+
});
|
|
2819
|
+
app.post("/api/chat", async (c) => {
|
|
2820
|
+
try {
|
|
2821
|
+
const body = await c.req.json();
|
|
2822
|
+
if (!body.messages || !Array.isArray(body.messages)) {
|
|
2823
|
+
return c.json({ error: "messages array is required" }, 400);
|
|
2824
|
+
}
|
|
2825
|
+
const response = await chat(body.messages, body.systemPrompt);
|
|
2826
|
+
return c.json({
|
|
2827
|
+
content: response.content,
|
|
2828
|
+
usage: {
|
|
2829
|
+
inputTokens: response.inputTokens,
|
|
2830
|
+
outputTokens: response.outputTokens
|
|
2831
|
+
}
|
|
2832
|
+
});
|
|
2833
|
+
} catch (error) {
|
|
2834
|
+
console.error("Chat API error:", error);
|
|
2835
|
+
return c.json({ error: "Internal server error" }, 500);
|
|
2836
|
+
}
|
|
2837
|
+
});
|
|
2838
|
+
app.post("/api/chat/tools", requirePermission("chat:tools"), async (c) => {
|
|
2839
|
+
try {
|
|
2840
|
+
const body = await c.req.json();
|
|
2841
|
+
if (!body.messages || !Array.isArray(body.messages)) {
|
|
2842
|
+
return c.json({ error: "messages array is required" }, 400);
|
|
2843
|
+
}
|
|
2844
|
+
const toolsUsed = [];
|
|
2845
|
+
const response = await chatWithTools(body.messages, body.userId, (tool) => {
|
|
2846
|
+
toolsUsed.push(tool);
|
|
2847
|
+
});
|
|
2848
|
+
return c.json({
|
|
2849
|
+
content: response.content,
|
|
2850
|
+
toolsUsed,
|
|
2851
|
+
usage: {
|
|
2852
|
+
inputTokens: response.inputTokens,
|
|
2853
|
+
outputTokens: response.outputTokens
|
|
2854
|
+
}
|
|
2855
|
+
});
|
|
2856
|
+
} catch (error) {
|
|
2857
|
+
console.error("Chat with tools API error:", error);
|
|
2858
|
+
return c.json({ error: "Internal server error" }, 500);
|
|
2859
|
+
}
|
|
2860
|
+
});
|
|
2861
|
+
app.post("/api/ask", async (c) => {
|
|
2862
|
+
try {
|
|
2863
|
+
const body = await c.req.json();
|
|
2864
|
+
if (!body.message) {
|
|
2865
|
+
return c.json({ error: "message is required" }, 400);
|
|
2866
|
+
}
|
|
2867
|
+
const messages2 = [{ role: "user", content: body.message }];
|
|
2868
|
+
const response = body.useTools ? await chatWithTools(messages2) : await chat(messages2, body.systemPrompt);
|
|
2869
|
+
return c.json({
|
|
2870
|
+
content: response.content,
|
|
2871
|
+
toolsUsed: response.toolsUsed,
|
|
2872
|
+
usage: {
|
|
2873
|
+
inputTokens: response.inputTokens,
|
|
2874
|
+
outputTokens: response.outputTokens
|
|
2875
|
+
}
|
|
2876
|
+
});
|
|
2877
|
+
} catch (error) {
|
|
2878
|
+
console.error("Ask API error:", error);
|
|
2879
|
+
return c.json({ error: "Internal server error" }, 500);
|
|
2880
|
+
}
|
|
2881
|
+
});
|
|
2882
|
+
app.get("/api/conversations", async (c) => {
|
|
2883
|
+
try {
|
|
2884
|
+
const convos = await db.select().from(conversations).orderBy(desc2(conversations.updatedAt)).limit(50);
|
|
2885
|
+
return c.json(convos);
|
|
2886
|
+
} catch (error) {
|
|
2887
|
+
console.error("Error fetching conversations:", error);
|
|
2888
|
+
return c.json({ error: "Internal server error" }, 500);
|
|
2889
|
+
}
|
|
2890
|
+
});
|
|
2891
|
+
app.get("/api/conversations/:id", async (c) => {
|
|
2892
|
+
try {
|
|
2893
|
+
const id = c.req.param("id");
|
|
2894
|
+
const convo = await db.select().from(conversations).where(eq4(conversations.id, id)).limit(1);
|
|
2895
|
+
if (convo.length === 0) {
|
|
2896
|
+
return c.json({ error: "Conversation not found" }, 404);
|
|
2897
|
+
}
|
|
2898
|
+
const msgs = await db.select().from(messages).where(eq4(messages.conversationId, id)).orderBy(messages.createdAt);
|
|
2899
|
+
const decryptedMsgs = msgs.map((m) => {
|
|
2900
|
+
if (m.encrypted && m.content) {
|
|
2901
|
+
try {
|
|
2902
|
+
return { ...m, content: decryptField(m.content) ?? m.content };
|
|
2903
|
+
} catch {
|
|
2904
|
+
return m;
|
|
2905
|
+
}
|
|
2906
|
+
}
|
|
2907
|
+
return m;
|
|
2908
|
+
});
|
|
2909
|
+
return c.json({ conversation: convo[0], messages: decryptedMsgs });
|
|
2910
|
+
} catch (error) {
|
|
2911
|
+
console.error("Error fetching conversation:", error);
|
|
2912
|
+
return c.json({ error: "Internal server error" }, 500);
|
|
2913
|
+
}
|
|
2914
|
+
});
|
|
2915
|
+
app.get("/api/memories", async (c) => {
|
|
2916
|
+
try {
|
|
2917
|
+
const userId = c.req.query("userId");
|
|
2918
|
+
const limit = parseInt(c.req.query("limit") || "50");
|
|
2919
|
+
let query = db.select().from(memories).orderBy(desc2(memories.createdAt)).limit(limit);
|
|
2920
|
+
const mems = await query;
|
|
2921
|
+
return c.json(mems);
|
|
2922
|
+
} catch (error) {
|
|
2923
|
+
console.error("Error fetching memories:", error);
|
|
2924
|
+
return c.json({ error: "Internal server error" }, 500);
|
|
2925
|
+
}
|
|
2926
|
+
});
|
|
2927
|
+
app.post("/api/memories/search", async (c) => {
|
|
2928
|
+
try {
|
|
2929
|
+
const body = await c.req.json();
|
|
2930
|
+
if (!body.query) {
|
|
2931
|
+
return c.json({ error: "query is required" }, 400);
|
|
2932
|
+
}
|
|
2933
|
+
const results = await searchMemories(body.query, body.userId, body.limit || 5);
|
|
2934
|
+
return c.json(results);
|
|
2935
|
+
} catch (error) {
|
|
2936
|
+
console.error("Error searching memories:", error);
|
|
2937
|
+
return c.json({ error: "Internal server error" }, 500);
|
|
2938
|
+
}
|
|
2939
|
+
});
|
|
2940
|
+
app.post("/api/memories", async (c) => {
|
|
2941
|
+
try {
|
|
2942
|
+
const body = await c.req.json();
|
|
2943
|
+
if (!body.content) {
|
|
2944
|
+
return c.json({ error: "content is required" }, 400);
|
|
2945
|
+
}
|
|
2946
|
+
const memory = await storeMemory({
|
|
2947
|
+
content: body.content,
|
|
2948
|
+
type: body.type || "semantic",
|
|
2949
|
+
importance: body.importance || 5,
|
|
2950
|
+
userId: body.userId,
|
|
2951
|
+
source: "api",
|
|
2952
|
+
provenance: "api:manual"
|
|
2953
|
+
});
|
|
2954
|
+
return c.json(memory);
|
|
2955
|
+
} catch (error) {
|
|
2956
|
+
console.error("Error storing memory:", error);
|
|
2957
|
+
return c.json({ error: "Internal server error" }, 500);
|
|
2958
|
+
}
|
|
2959
|
+
});
|
|
2960
|
+
app.get("/api/memories/export", async (c) => {
|
|
2961
|
+
try {
|
|
2962
|
+
const userId = c.req.query("userId");
|
|
2963
|
+
const format = c.req.query("format") || "markdown";
|
|
2964
|
+
const exported = await exportMemories(userId || void 0, format);
|
|
2965
|
+
if (format === "json") {
|
|
2966
|
+
return c.json(JSON.parse(exported));
|
|
2967
|
+
}
|
|
2968
|
+
return c.text(exported);
|
|
2969
|
+
} catch (error) {
|
|
2970
|
+
console.error("Error exporting memories:", error);
|
|
2971
|
+
return c.json({ error: "Internal server error" }, 500);
|
|
2972
|
+
}
|
|
2973
|
+
});
|
|
2974
|
+
app.get("/api/memories/:id", async (c) => {
|
|
2975
|
+
try {
|
|
2976
|
+
const id = c.req.param("id");
|
|
2977
|
+
const memory = await getMemoryById(id);
|
|
2978
|
+
if (!memory) {
|
|
2979
|
+
return c.json({ error: "Memory not found" }, 404);
|
|
2980
|
+
}
|
|
2981
|
+
return c.json(memory);
|
|
2982
|
+
} catch (error) {
|
|
2983
|
+
console.error("Error fetching memory:", error);
|
|
2984
|
+
return c.json({ error: "Internal server error" }, 500);
|
|
2985
|
+
}
|
|
2986
|
+
});
|
|
2987
|
+
app.put("/api/memories/:id", async (c) => {
|
|
2988
|
+
try {
|
|
2989
|
+
const id = c.req.param("id");
|
|
2990
|
+
const body = await c.req.json();
|
|
2991
|
+
const updated = await updateMemory(id, body);
|
|
2992
|
+
if (!updated) {
|
|
2993
|
+
return c.json({ error: "Memory not found or no changes" }, 404);
|
|
2994
|
+
}
|
|
2995
|
+
return c.json(updated);
|
|
2996
|
+
} catch (error) {
|
|
2997
|
+
console.error("Error updating memory:", error);
|
|
2998
|
+
return c.json({ error: "Internal server error" }, 500);
|
|
2999
|
+
}
|
|
3000
|
+
});
|
|
3001
|
+
app.delete("/api/memories/:id", requirePermission("memories:delete"), async (c) => {
|
|
3002
|
+
try {
|
|
3003
|
+
const id = c.req.param("id");
|
|
3004
|
+
const deleted = await deleteMemory(id);
|
|
3005
|
+
if (!deleted) {
|
|
3006
|
+
return c.json({ error: "Memory not found" }, 404);
|
|
3007
|
+
}
|
|
3008
|
+
return c.json({ success: true, id });
|
|
3009
|
+
} catch (error) {
|
|
3010
|
+
console.error("Error deleting memory:", error);
|
|
3011
|
+
return c.json({ error: "Internal server error" }, 500);
|
|
3012
|
+
}
|
|
3013
|
+
});
|
|
3014
|
+
app.get("/api/autonomy", async (c) => {
|
|
3015
|
+
try {
|
|
3016
|
+
const { autonomyManager } = await import("./autonomy-N7W5XPLX.js");
|
|
3017
|
+
const userId = c.req.query("userId");
|
|
3018
|
+
return c.json({
|
|
3019
|
+
level: userId ? autonomyManager.getLevel(userId) : autonomyManager.getDefaultLevel(),
|
|
3020
|
+
stats: autonomyManager.getStats()
|
|
3021
|
+
});
|
|
3022
|
+
} catch (error) {
|
|
3023
|
+
return c.json({ error: "Autonomy system not available" }, 500);
|
|
3024
|
+
}
|
|
3025
|
+
});
|
|
3026
|
+
app.put("/api/autonomy", async (c) => {
|
|
3027
|
+
try {
|
|
3028
|
+
const { autonomyManager } = await import("./autonomy-N7W5XPLX.js");
|
|
3029
|
+
const body = await c.req.json();
|
|
3030
|
+
const level = body.level;
|
|
3031
|
+
if (!["readonly", "supervised", "autonomous"].includes(level)) {
|
|
3032
|
+
return c.json({ error: "Invalid level. Must be: readonly, supervised, or autonomous" }, 400);
|
|
3033
|
+
}
|
|
3034
|
+
if (body.userId) {
|
|
3035
|
+
autonomyManager.setLevel(body.userId, level);
|
|
3036
|
+
} else {
|
|
3037
|
+
autonomyManager.setDefaultLevel(level);
|
|
3038
|
+
}
|
|
3039
|
+
return c.json({ success: true, level });
|
|
3040
|
+
} catch (error) {
|
|
3041
|
+
return c.json({ error: "Autonomy system not available" }, 500);
|
|
3042
|
+
}
|
|
3043
|
+
});
|
|
3044
|
+
app.get("/metrics", async (c) => {
|
|
3045
|
+
try {
|
|
3046
|
+
const { prometheusExporter } = await import("./prometheus-YETCZO4I.js");
|
|
3047
|
+
const text = prometheusExporter.toTextFormat();
|
|
3048
|
+
return c.text(text, 200, { "Content-Type": "text/plain; version=0.0.4; charset=utf-8" });
|
|
3049
|
+
} catch (error) {
|
|
3050
|
+
return c.text("# Prometheus metrics not available\n", 500);
|
|
3051
|
+
}
|
|
3052
|
+
});
|
|
3053
|
+
app.get("/api/metrics/prometheus", async (c) => {
|
|
3054
|
+
try {
|
|
3055
|
+
const { prometheusExporter } = await import("./prometheus-YETCZO4I.js");
|
|
3056
|
+
const text = prometheusExporter.toTextFormat();
|
|
3057
|
+
return c.text(text, 200, { "Content-Type": "text/plain; version=0.0.4; charset=utf-8" });
|
|
3058
|
+
} catch (error) {
|
|
3059
|
+
return c.text("# Prometheus metrics not available\n", 500);
|
|
3060
|
+
}
|
|
3061
|
+
});
|
|
3062
|
+
app.post("/api/pair", async (c) => {
|
|
3063
|
+
try {
|
|
3064
|
+
const { pairingManager } = await import("./pairing-77N47RAT.js");
|
|
3065
|
+
const body = await c.req.json();
|
|
3066
|
+
if (!body.code) {
|
|
3067
|
+
return c.json({ error: "code is required" }, 400);
|
|
3068
|
+
}
|
|
3069
|
+
const result = pairingManager.pair(body.code, body.deviceInfo || "Unknown device");
|
|
3070
|
+
if (!result.success) {
|
|
3071
|
+
return c.json({ error: result.error }, 401);
|
|
3072
|
+
}
|
|
3073
|
+
return c.json({ token: result.token });
|
|
3074
|
+
} catch (error) {
|
|
3075
|
+
return c.json({ error: "Pairing system not available" }, 500);
|
|
3076
|
+
}
|
|
3077
|
+
});
|
|
3078
|
+
app.get("/api/providers", async (c) => {
|
|
3079
|
+
try {
|
|
3080
|
+
const { providerRegistry } = await import("./providers-H6YIC3MG.js");
|
|
3081
|
+
return c.json({
|
|
3082
|
+
providers: providerRegistry.listProviders(),
|
|
3083
|
+
default: providerRegistry.getDefaultId()
|
|
3084
|
+
});
|
|
3085
|
+
} catch (error) {
|
|
3086
|
+
return c.json({ error: "Provider system not available" }, 500);
|
|
3087
|
+
}
|
|
3088
|
+
});
|
|
3089
|
+
app.route("/api/osint", osint);
|
|
3090
|
+
app.route("/api/email", email);
|
|
3091
|
+
app.route("/api/sdk", sdkRoutes);
|
|
3092
|
+
app.route("/api/admin", admin_default);
|
|
3093
|
+
app.get("/api/audit-logs", async (c) => {
|
|
3094
|
+
try {
|
|
3095
|
+
const { queryAuditLogs: queryAuditLogs2 } = await import("./audit-logger-AU3TMWKI.js");
|
|
3096
|
+
const url = new URL(c.req.url);
|
|
3097
|
+
const options = {};
|
|
3098
|
+
const userId = url.searchParams.get("userId");
|
|
3099
|
+
if (userId) options.userId = userId;
|
|
3100
|
+
const action = url.searchParams.get("action");
|
|
3101
|
+
if (action) options.action = action;
|
|
3102
|
+
const resource = url.searchParams.get("resource");
|
|
3103
|
+
if (resource) options.resource = resource;
|
|
3104
|
+
const startDate = url.searchParams.get("startDate");
|
|
3105
|
+
if (startDate) options.startDate = new Date(startDate);
|
|
3106
|
+
const endDate = url.searchParams.get("endDate");
|
|
3107
|
+
if (endDate) options.endDate = new Date(endDate);
|
|
3108
|
+
const limit = url.searchParams.get("limit");
|
|
3109
|
+
if (limit) options.limit = parseInt(limit);
|
|
3110
|
+
const offset = url.searchParams.get("offset");
|
|
3111
|
+
if (offset) options.offset = parseInt(offset);
|
|
3112
|
+
const logs = await queryAuditLogs2(options);
|
|
3113
|
+
return c.json({ success: true, logs });
|
|
3114
|
+
} catch (err) {
|
|
3115
|
+
return c.json({ success: false, error: err?.message || "Failed to query audit logs", logs: [] });
|
|
3116
|
+
}
|
|
3117
|
+
});
|
|
3118
|
+
app.get("/api/audit-logs/integrity", async (c) => {
|
|
3119
|
+
try {
|
|
3120
|
+
const { getAuditChainIntegrity: getAuditChainIntegrity2 } = await import("./audit-logger-AU3TMWKI.js");
|
|
3121
|
+
const integrity = await getAuditChainIntegrity2();
|
|
3122
|
+
return c.json({ success: true, ...integrity });
|
|
3123
|
+
} catch (err) {
|
|
3124
|
+
return c.json({ success: false, error: err?.message, chainValid: false, totalEntries: 0, lastSequence: 0 });
|
|
3125
|
+
}
|
|
3126
|
+
});
|
|
3127
|
+
app.delete("/api/audit-logs", async (c) => {
|
|
3128
|
+
try {
|
|
3129
|
+
const { auditLogs } = await import("./schema-ALJ67YVG.js");
|
|
3130
|
+
await db.delete(auditLogs);
|
|
3131
|
+
return c.json({ success: true });
|
|
3132
|
+
} catch (err) {
|
|
3133
|
+
return c.json({ error: err?.message || "Failed to clear audit logs" }, 500);
|
|
3134
|
+
}
|
|
3135
|
+
});
|
|
3136
|
+
app.delete("/api/conversations", async (c) => {
|
|
3137
|
+
try {
|
|
3138
|
+
await db.delete(messages);
|
|
3139
|
+
await db.delete(conversations);
|
|
3140
|
+
return c.json({ success: true });
|
|
3141
|
+
} catch (err) {
|
|
3142
|
+
return c.json({ error: err?.message || "Failed to clear sessions" }, 500);
|
|
3143
|
+
}
|
|
3144
|
+
});
|
|
3145
|
+
app.delete("/api/conversations/:id", async (c) => {
|
|
3146
|
+
try {
|
|
3147
|
+
const id = c.req.param("id");
|
|
3148
|
+
const convo = await db.select().from(conversations).where(eq4(conversations.id, id)).limit(1);
|
|
3149
|
+
if (convo.length === 0) {
|
|
3150
|
+
return c.json({ error: "Conversation not found" }, 404);
|
|
3151
|
+
}
|
|
3152
|
+
await db.delete(messages).where(eq4(messages.conversationId, id));
|
|
3153
|
+
await db.delete(conversations).where(eq4(conversations.id, id));
|
|
3154
|
+
return c.json({ success: true, id });
|
|
3155
|
+
} catch (err) {
|
|
3156
|
+
return c.json({ error: err?.message || "Failed to delete conversation" }, 500);
|
|
3157
|
+
}
|
|
3158
|
+
});
|
|
3159
|
+
app.route("/api/brain", brain_default);
|
|
3160
|
+
app.route("/api/scheduler", scheduler_default);
|
|
3161
|
+
app.route("/api/alerts", alerts_default);
|
|
3162
|
+
app.route("/api/webhooks", webhooks_default);
|
|
3163
|
+
app.route("/api/github", github_default);
|
|
3164
|
+
app.route("/api/metrics", metrics_default);
|
|
3165
|
+
app.get("/api/metrics/overview", async (c) => {
|
|
3166
|
+
try {
|
|
3167
|
+
const { getMetricAggregates: getMetricAggregates2 } = await import("./metrics-VJDWQWU7.js");
|
|
3168
|
+
const { getErrorStats: getErrorStats2 } = await import("./error-tracker-SVQSDQDW.js");
|
|
3169
|
+
const now = /* @__PURE__ */ new Date();
|
|
3170
|
+
const dayAgo = new Date(now.getTime() - 24 * 60 * 60 * 1e3);
|
|
3171
|
+
const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1e3);
|
|
3172
|
+
const [latencyDay, inputDay, outputDay, errorsDay, inputWeek, outputWeek] = await Promise.allSettled([
|
|
3173
|
+
getMetricAggregates2("response_latency", dayAgo, now),
|
|
3174
|
+
getMetricAggregates2("token_usage_input", dayAgo, now),
|
|
3175
|
+
getMetricAggregates2("token_usage_output", dayAgo, now),
|
|
3176
|
+
getErrorStats2(dayAgo, now),
|
|
3177
|
+
getMetricAggregates2("token_usage_input", weekAgo, now),
|
|
3178
|
+
getMetricAggregates2("token_usage_output", weekAgo, now)
|
|
3179
|
+
]);
|
|
3180
|
+
const val = (r, key) => r.status === "fulfilled" ? r.value?.[key] ?? 0 : 0;
|
|
3181
|
+
return c.json({
|
|
3182
|
+
last24h: {
|
|
3183
|
+
inputTokens: val(inputDay, "sum"),
|
|
3184
|
+
outputTokens: val(outputDay, "sum"),
|
|
3185
|
+
requests: val(latencyDay, "count"),
|
|
3186
|
+
avgLatencyMs: val(latencyDay, "avg"),
|
|
3187
|
+
errors: errorsDay.status === "fulfilled" ? errorsDay.value?.total ?? 0 : 0
|
|
3188
|
+
},
|
|
3189
|
+
last7d: {
|
|
3190
|
+
inputTokens: val(inputWeek, "sum"),
|
|
3191
|
+
outputTokens: val(outputWeek, "sum")
|
|
3192
|
+
}
|
|
3193
|
+
});
|
|
3194
|
+
} catch {
|
|
3195
|
+
return c.json({ last24h: {}, last7d: {} });
|
|
3196
|
+
}
|
|
3197
|
+
});
|
|
3198
|
+
app.route("/api/mcp", mcp_default);
|
|
3199
|
+
app.route("/api/bots", bots_default);
|
|
3200
|
+
app.route("/api/users", users_default);
|
|
3201
|
+
app.get("/api/incidents", requirePermission("admin:settings"), async (c) => {
|
|
3202
|
+
try {
|
|
3203
|
+
const { getOpenIncidents } = await import("./incident-response-ZTIKUWEO.js");
|
|
3204
|
+
const severity = c.req.query("severity");
|
|
3205
|
+
const type = c.req.query("type");
|
|
3206
|
+
const incidents = await getOpenIncidents({ severity, type, limit: 50 });
|
|
3207
|
+
return c.json(incidents);
|
|
3208
|
+
} catch (error) {
|
|
3209
|
+
return c.json({ error: "Incident system not available" }, 500);
|
|
3210
|
+
}
|
|
3211
|
+
});
|
|
3212
|
+
app.get("/api/incidents/:id", requirePermission("admin:settings"), async (c) => {
|
|
3213
|
+
try {
|
|
3214
|
+
const { generateIncidentReport } = await import("./incident-response-ZTIKUWEO.js");
|
|
3215
|
+
const id = c.req.param("id");
|
|
3216
|
+
const report = await generateIncidentReport(id);
|
|
3217
|
+
return c.json(report);
|
|
3218
|
+
} catch (error) {
|
|
3219
|
+
return c.json({ error: "Incident not found" }, 404);
|
|
3220
|
+
}
|
|
3221
|
+
});
|
|
3222
|
+
app.post("/api/incidents/:id/status", requirePermission("admin:settings"), async (c) => {
|
|
3223
|
+
try {
|
|
3224
|
+
const { updateIncidentStatus } = await import("./incident-response-ZTIKUWEO.js");
|
|
3225
|
+
const id = c.req.param("id");
|
|
3226
|
+
const body = await c.req.json();
|
|
3227
|
+
const userId = getAuthUserId(c);
|
|
3228
|
+
const updated = await updateIncidentStatus(id, body.status, userId, body.notes);
|
|
3229
|
+
return c.json(updated);
|
|
3230
|
+
} catch (error) {
|
|
3231
|
+
return c.json({ error: "Failed to update incident" }, 500);
|
|
3232
|
+
}
|
|
3233
|
+
});
|
|
3234
|
+
app.post("/api/incidents/:id/resolve", requirePermission("admin:settings"), async (c) => {
|
|
3235
|
+
try {
|
|
3236
|
+
const { resolveIncident } = await import("./incident-response-ZTIKUWEO.js");
|
|
3237
|
+
const id = c.req.param("id");
|
|
3238
|
+
const body = await c.req.json();
|
|
3239
|
+
const userId = getAuthUserId(c);
|
|
3240
|
+
const resolved = await resolveIncident(id, body.notes, userId);
|
|
3241
|
+
return c.json(resolved);
|
|
3242
|
+
} catch (error) {
|
|
3243
|
+
return c.json({ error: "Failed to resolve incident" }, 500);
|
|
3244
|
+
}
|
|
3245
|
+
});
|
|
3246
|
+
app.get("/api/audit/integrity", requirePermission("admin:settings"), async (c) => {
|
|
3247
|
+
try {
|
|
3248
|
+
const { getAuditChainIntegrity: getAuditChainIntegrity2 } = await import("./audit-logger-AU3TMWKI.js");
|
|
3249
|
+
const integrity = await getAuditChainIntegrity2();
|
|
3250
|
+
return c.json(integrity);
|
|
3251
|
+
} catch (error) {
|
|
3252
|
+
return c.json({ error: "Audit system not available" }, 500);
|
|
3253
|
+
}
|
|
3254
|
+
});
|
|
3255
|
+
app.post("/api/transcribe", async (c) => {
|
|
3256
|
+
try {
|
|
3257
|
+
const formData = await c.req.formData();
|
|
3258
|
+
const audioFile = formData.get("audio");
|
|
3259
|
+
if (!audioFile || !(audioFile instanceof File)) {
|
|
3260
|
+
return c.json({ error: "audio file is required" }, 400);
|
|
3261
|
+
}
|
|
3262
|
+
const buffer = Buffer.from(await audioFile.arrayBuffer());
|
|
3263
|
+
const text = await transcribeAudio(buffer);
|
|
3264
|
+
if (!text) {
|
|
3265
|
+
return c.json({ error: "Could not transcribe audio" }, 500);
|
|
3266
|
+
}
|
|
3267
|
+
return c.json({ text });
|
|
3268
|
+
} catch (error) {
|
|
3269
|
+
console.error("Transcribe API error:", error);
|
|
3270
|
+
return c.json({ error: "Transcription failed" }, 500);
|
|
3271
|
+
}
|
|
3272
|
+
});
|
|
3273
|
+
app.post("/api/tts", async (c) => {
|
|
3274
|
+
try {
|
|
3275
|
+
const body = await c.req.json();
|
|
3276
|
+
if (!body.text) {
|
|
3277
|
+
return c.json({ error: "text is required" }, 400);
|
|
3278
|
+
}
|
|
3279
|
+
const audioBuffer = await textToSpeech(body.text);
|
|
3280
|
+
if (!audioBuffer) {
|
|
3281
|
+
return c.json({ error: "Text-to-speech failed" }, 500);
|
|
3282
|
+
}
|
|
3283
|
+
return new Response(new Uint8Array(audioBuffer), {
|
|
3284
|
+
headers: {
|
|
3285
|
+
"Content-Type": "audio/mpeg",
|
|
3286
|
+
"Content-Length": String(audioBuffer.length)
|
|
3287
|
+
}
|
|
3288
|
+
});
|
|
3289
|
+
} catch (error) {
|
|
3290
|
+
console.error("TTS API error:", error);
|
|
3291
|
+
return c.json({ error: "TTS failed" }, 500);
|
|
3292
|
+
}
|
|
3293
|
+
});
|
|
3294
|
+
app.get("/api/system/status", async (c) => {
|
|
3295
|
+
return c.json({
|
|
3296
|
+
status: "online",
|
|
3297
|
+
version: "3.4.0",
|
|
3298
|
+
uptime: process.uptime(),
|
|
3299
|
+
memory: process.memoryUsage()
|
|
3300
|
+
});
|
|
3301
|
+
});
|
|
3302
|
+
app.get("/api/callbacks/spotify", async (c) => {
|
|
3303
|
+
try {
|
|
3304
|
+
const code = c.req.query("code");
|
|
3305
|
+
const error = c.req.query("error");
|
|
3306
|
+
if (error) {
|
|
3307
|
+
return c.html(`<h1>Spotify Authorization Failed</h1><p>Error: ${error}</p><p>Go back and try again.</p>`);
|
|
3308
|
+
}
|
|
3309
|
+
if (!code) {
|
|
3310
|
+
return c.html(`<h1>Missing Authorization Code</h1><p>No code received from Spotify.</p>`);
|
|
3311
|
+
}
|
|
3312
|
+
const { env: appEnv } = await import("./env-CHOFICED.js");
|
|
3313
|
+
if (!appEnv.SPOTIFY_CLIENT_ID || !appEnv.SPOTIFY_CLIENT_SECRET) {
|
|
3314
|
+
return c.html(`<h1>Spotify Not Configured</h1><p>Set SPOTIFY_CLIENT_ID and SPOTIFY_CLIENT_SECRET in .env</p>`);
|
|
3315
|
+
}
|
|
3316
|
+
const { createSpotifyAuth } = await import("./auth-PH5IHISW.js");
|
|
3317
|
+
const auth = createSpotifyAuth({
|
|
3318
|
+
clientId: appEnv.SPOTIFY_CLIENT_ID,
|
|
3319
|
+
clientSecret: appEnv.SPOTIFY_CLIENT_SECRET,
|
|
3320
|
+
redirectUri: appEnv.SPOTIFY_REDIRECT_URI || `http://localhost:${appEnv.PORT}/api/callbacks/spotify`
|
|
3321
|
+
});
|
|
3322
|
+
const tokens = await auth.exchangeCode(code);
|
|
3323
|
+
return c.html(`
|
|
3324
|
+
<html><body style="font-family: system-ui; max-width: 600px; margin: 40px auto; padding: 20px;">
|
|
3325
|
+
<h1>Spotify Connected!</h1>
|
|
3326
|
+
<p>Add this to your <code>.env</code> file:</p>
|
|
3327
|
+
<pre style="background: #f0f0f0; padding: 16px; border-radius: 8px; word-break: break-all; white-space: pre-wrap;">SPOTIFY_REFRESH_TOKEN=${tokens.refreshToken}</pre>
|
|
3328
|
+
<p>Then restart OpenSentinel. You can close this page.</p>
|
|
3329
|
+
</body></html>
|
|
3330
|
+
`);
|
|
3331
|
+
} catch (err) {
|
|
3332
|
+
console.error("Spotify callback error:", err);
|
|
3333
|
+
return c.html(`<h1>Spotify Auth Error</h1><p>${err instanceof Error ? err.message : String(err)}</p>`);
|
|
3334
|
+
}
|
|
3335
|
+
});
|
|
3336
|
+
app.get("/api/spotify/authorize", async (c) => {
|
|
3337
|
+
try {
|
|
3338
|
+
const { env: appEnv } = await import("./env-CHOFICED.js");
|
|
3339
|
+
if (!appEnv.SPOTIFY_CLIENT_ID || !appEnv.SPOTIFY_CLIENT_SECRET) {
|
|
3340
|
+
return c.json({ error: "Spotify not configured. Set SPOTIFY_CLIENT_ID and SPOTIFY_CLIENT_SECRET in .env" }, 400);
|
|
3341
|
+
}
|
|
3342
|
+
const { createSpotifyAuth, DEFAULT_SCOPES } = await import("./auth-PH5IHISW.js");
|
|
3343
|
+
const auth = createSpotifyAuth({
|
|
3344
|
+
clientId: appEnv.SPOTIFY_CLIENT_ID,
|
|
3345
|
+
clientSecret: appEnv.SPOTIFY_CLIENT_SECRET,
|
|
3346
|
+
redirectUri: appEnv.SPOTIFY_REDIRECT_URI || `http://localhost:${appEnv.PORT}/api/callbacks/spotify`
|
|
3347
|
+
});
|
|
3348
|
+
const url = auth.getAuthorizationUrl(DEFAULT_SCOPES, void 0, true);
|
|
3349
|
+
return c.json({ url });
|
|
3350
|
+
} catch (err) {
|
|
3351
|
+
return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
|
|
3352
|
+
}
|
|
3353
|
+
});
|
|
3354
|
+
app.get("/api/files/download/:token", async (c) => {
|
|
3355
|
+
const token = c.req.param("token");
|
|
3356
|
+
const entry = getDownloadEntry(token);
|
|
3357
|
+
if (!entry) {
|
|
3358
|
+
return c.json({ error: "Download link expired or invalid" }, 404);
|
|
3359
|
+
}
|
|
3360
|
+
const tmp = resolve2(tmpdir());
|
|
3361
|
+
if (!entry.filePath.startsWith(tmp)) {
|
|
3362
|
+
return c.json({ error: "Access denied" }, 403);
|
|
3363
|
+
}
|
|
3364
|
+
try {
|
|
3365
|
+
const file = Bun.file(entry.filePath);
|
|
3366
|
+
if (!await file.exists()) {
|
|
3367
|
+
return c.json({ error: "File not found" }, 404);
|
|
3368
|
+
}
|
|
3369
|
+
return new Response(file, {
|
|
3370
|
+
headers: {
|
|
3371
|
+
"Content-Type": entry.contentType,
|
|
3372
|
+
"Content-Disposition": `attachment; filename="${entry.filename}"`
|
|
3373
|
+
}
|
|
3374
|
+
});
|
|
3375
|
+
} catch {
|
|
3376
|
+
return c.json({ error: "Failed to serve file" }, 500);
|
|
3377
|
+
}
|
|
3378
|
+
});
|
|
3379
|
+
app.post("/api/files/upload", async (c) => {
|
|
3380
|
+
try {
|
|
3381
|
+
const formData = await c.req.formData();
|
|
3382
|
+
const file = formData.get("file");
|
|
3383
|
+
if (!file || !(file instanceof File)) {
|
|
3384
|
+
return c.json({ error: "file field is required" }, 400);
|
|
3385
|
+
}
|
|
3386
|
+
if (file.size > 50 * 1024 * 1024) {
|
|
3387
|
+
return c.json({ error: "File too large (50MB max)" }, 413);
|
|
3388
|
+
}
|
|
3389
|
+
const ext = file.name.split(".").pop() || "bin";
|
|
3390
|
+
const id = randomBytes3(8).toString("hex");
|
|
3391
|
+
const filename = `sentinel-upload-${id}.${ext}`;
|
|
3392
|
+
const filePath = join(tmpdir(), filename);
|
|
3393
|
+
const buffer = Buffer.from(await file.arrayBuffer());
|
|
3394
|
+
await Bun.write(filePath, buffer);
|
|
3395
|
+
return c.json({ filePath, originalName: file.name });
|
|
3396
|
+
} catch {
|
|
3397
|
+
return c.json({ error: "Upload failed" }, 500);
|
|
3398
|
+
}
|
|
3399
|
+
});
|
|
3400
|
+
app.use("/*", serveStatic({ root: "./src/web/dist" }));
|
|
3401
|
+
app.get("/*", serveStatic({ path: "./src/web/dist/index.html" }));
|
|
3402
|
+
|
|
3403
|
+
export {
|
|
3404
|
+
getGatewayToken,
|
|
3405
|
+
timingSafeEqual,
|
|
3406
|
+
app
|
|
3407
|
+
};
|
|
3408
|
+
//# sourceMappingURL=chunk-AD6YEH6U.js.map
|