opensentinel 3.1.1 → 3.7.0

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