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 @@
1
+ {"version":3,"sources":["../src/integrations/github/client.ts","../src/integrations/github/pull-requests.ts","../src/integrations/github/code-review.ts"],"sourcesContent":["/**\n * GitHub API Client\n *\n * Provides authenticated access to the GitHub API using Octokit.\n */\n\nimport { Octokit } from \"octokit\";\nimport { env } from \"../../config/env\";\n\nexport interface GitHubClientConfig {\n token?: string;\n baseUrl?: string;\n}\n\nlet octokitInstance: Octokit | null = null;\n\n/**\n * Get or create the Octokit instance\n */\nexport function getOctokit(config?: GitHubClientConfig): Octokit {\n const token = config?.token || env.GITHUB_TOKEN;\n\n if (!token) {\n throw new Error(\"GitHub token is required. Set GITHUB_TOKEN environment variable.\");\n }\n\n if (!octokitInstance || config?.token) {\n octokitInstance = new Octokit({\n auth: token,\n baseUrl: config?.baseUrl,\n });\n }\n\n return octokitInstance;\n}\n\n/**\n * Create a new Octokit instance with specific config\n */\nexport function createOctokit(config: GitHubClientConfig): Octokit {\n const token = config.token || env.GITHUB_TOKEN;\n\n if (!token) {\n throw new Error(\"GitHub token is required.\");\n }\n\n return new Octokit({\n auth: token,\n baseUrl: config.baseUrl,\n });\n}\n\n/**\n * Get authenticated user information\n */\nexport async function getAuthenticatedUser(config?: GitHubClientConfig): Promise<{\n login: string;\n id: number;\n name: string | null;\n email: string | null;\n avatarUrl: string;\n}> {\n const octokit = getOctokit(config);\n\n const { data } = await octokit.rest.users.getAuthenticated();\n\n return {\n login: data.login,\n id: data.id,\n name: data.name,\n email: data.email,\n avatarUrl: data.avatar_url,\n };\n}\n\n/**\n * Check rate limit status\n */\nexport async function getRateLimit(config?: GitHubClientConfig): Promise<{\n limit: number;\n remaining: number;\n reset: Date;\n used: number;\n}> {\n const octokit = getOctokit(config);\n\n const { data } = await octokit.rest.rateLimit.get();\n\n return {\n limit: data.rate.limit,\n remaining: data.rate.remaining,\n reset: new Date(data.rate.reset * 1000),\n used: data.rate.used,\n };\n}\n\n/**\n * Parse owner and repo from a repository URL or string\n */\nexport function parseRepoString(repoString: string): { owner: string; repo: string } {\n // Handle full GitHub URLs\n if (repoString.includes(\"github.com\")) {\n const match = repoString.match(/github\\.com[/:]([\\w.-]+)\\/([\\w.-]+)/);\n if (match) {\n return { owner: match[1], repo: match[2].replace(/\\.git$/, \"\") };\n }\n }\n\n // Handle owner/repo format\n const parts = repoString.split(\"/\");\n if (parts.length === 2) {\n return { owner: parts[0], repo: parts[1] };\n }\n\n throw new Error(`Invalid repository string: ${repoString}. Expected format: owner/repo or GitHub URL`);\n}\n\nexport { Octokit };\n","/**\r\n * GitHub Pull Request Operations\r\n *\r\n * Provides functions for creating, reviewing, and merging pull requests.\r\n */\r\n\r\nimport { getOctokit, parseRepoString, type GitHubClientConfig } from \"./client\";\r\n\r\nexport interface PullRequest {\r\n id: number;\r\n number: number;\r\n title: string;\r\n body: string | null;\r\n state: \"open\" | \"closed\";\r\n htmlUrl: string;\r\n diffUrl: string;\r\n patchUrl: string;\r\n draft: boolean;\r\n merged: boolean;\r\n mergeable: boolean | null;\r\n mergeableState: string;\r\n mergedAt: string | null;\r\n mergedBy: {\r\n login: string;\r\n id: number;\r\n avatarUrl: string;\r\n } | null;\r\n user: {\r\n login: string;\r\n id: number;\r\n avatarUrl: string;\r\n } | null;\r\n head: {\r\n ref: string;\r\n sha: string;\r\n repo: {\r\n fullName: string;\r\n cloneUrl: string;\r\n } | null;\r\n };\r\n base: {\r\n ref: string;\r\n sha: string;\r\n repo: {\r\n fullName: string;\r\n cloneUrl: string;\r\n } | null;\r\n };\r\n labels: Array<{\r\n id: number;\r\n name: string;\r\n color: string;\r\n description: string | null;\r\n }>;\r\n assignees: Array<{\r\n login: string;\r\n id: number;\r\n avatarUrl: string;\r\n }>;\r\n requestedReviewers: Array<{\r\n login: string;\r\n id: number;\r\n avatarUrl: string;\r\n }>;\r\n requestedTeams: Array<{\r\n id: number;\r\n name: string;\r\n slug: string;\r\n }>;\r\n milestone: {\r\n id: number;\r\n number: number;\r\n title: string;\r\n state: string;\r\n } | null;\r\n additions: number;\r\n deletions: number;\r\n changedFiles: number;\r\n commits: number;\r\n comments: number;\r\n reviewComments: number;\r\n createdAt: string;\r\n updatedAt: string;\r\n closedAt: string | null;\r\n}\r\n\r\nexport interface PullRequestReview {\r\n id: number;\r\n user: {\r\n login: string;\r\n id: number;\r\n avatarUrl: string;\r\n } | null;\r\n body: string | null;\r\n state: \"APPROVED\" | \"CHANGES_REQUESTED\" | \"COMMENTED\" | \"DISMISSED\" | \"PENDING\";\r\n htmlUrl: string;\r\n submittedAt: string | null;\r\n commitId: string;\r\n}\r\n\r\nexport interface ReviewComment {\r\n id: number;\r\n pullRequestReviewId: number | null;\r\n diffHunk: string;\r\n path: string;\r\n position: number | null;\r\n originalPosition: number | null;\r\n commitId: string;\r\n originalCommitId: string;\r\n user: {\r\n login: string;\r\n id: number;\r\n avatarUrl: string;\r\n } | null;\r\n body: string;\r\n htmlUrl: string;\r\n createdAt: string;\r\n updatedAt: string;\r\n line: number | null;\r\n side: \"LEFT\" | \"RIGHT\";\r\n startLine: number | null;\r\n startSide: \"LEFT\" | \"RIGHT\" | null;\r\n inReplyToId: number | null;\r\n}\r\n\r\nexport interface PullRequestFile {\r\n sha: string;\r\n filename: string;\r\n status: \"added\" | \"removed\" | \"modified\" | \"renamed\" | \"copied\" | \"changed\" | \"unchanged\";\r\n additions: number;\r\n deletions: number;\r\n changes: number;\r\n blobUrl: string;\r\n rawUrl: string;\r\n contentsUrl: string;\r\n patch?: string;\r\n previousFilename?: string;\r\n}\r\n\r\nexport interface CreatePullRequestOptions {\r\n title: string;\r\n body?: string;\r\n head: string;\r\n base: string;\r\n draft?: boolean;\r\n maintainerCanModify?: boolean;\r\n}\r\n\r\nexport interface UpdatePullRequestOptions {\r\n title?: string;\r\n body?: string;\r\n state?: \"open\" | \"closed\";\r\n base?: string;\r\n maintainerCanModify?: boolean;\r\n}\r\n\r\nexport interface ListPullRequestsOptions {\r\n state?: \"open\" | \"closed\" | \"all\";\r\n head?: string;\r\n base?: string;\r\n sort?: \"created\" | \"updated\" | \"popularity\" | \"long-running\";\r\n direction?: \"asc\" | \"desc\";\r\n perPage?: number;\r\n page?: number;\r\n}\r\n\r\nexport interface CreateReviewOptions {\r\n commitId?: string;\r\n body?: string;\r\n event?: \"APPROVE\" | \"REQUEST_CHANGES\" | \"COMMENT\";\r\n comments?: Array<{\r\n path: string;\r\n position?: number;\r\n body: string;\r\n line?: number;\r\n side?: \"LEFT\" | \"RIGHT\";\r\n startLine?: number;\r\n startSide?: \"LEFT\" | \"RIGHT\";\r\n }>;\r\n}\r\n\r\nexport interface MergeOptions {\r\n commitTitle?: string;\r\n commitMessage?: string;\r\n sha?: string;\r\n mergeMethod?: \"merge\" | \"squash\" | \"rebase\";\r\n}\r\n\r\n/**\r\n * List pull requests for a repository\r\n */\r\nexport async function listPullRequests(\r\n repoString: string,\r\n options: ListPullRequestsOptions = {},\r\n config?: GitHubClientConfig\r\n): Promise<PullRequest[]> {\r\n const octokit = getOctokit(config);\r\n const { owner, repo } = parseRepoString(repoString);\r\n\r\n const { data } = await octokit.rest.pulls.list({\r\n owner,\r\n repo,\r\n state: options.state || \"open\",\r\n head: options.head,\r\n base: options.base,\r\n sort: options.sort || \"created\",\r\n direction: options.direction || \"desc\",\r\n per_page: options.perPage || 30,\r\n page: options.page || 1,\r\n });\r\n\r\n return data.map(mapPullRequest);\r\n}\r\n\r\n/**\r\n * Get a specific pull request by number\r\n */\r\nexport async function getPullRequest(\r\n repoString: string,\r\n prNumber: number,\r\n config?: GitHubClientConfig\r\n): Promise<PullRequest> {\r\n const octokit = getOctokit(config);\r\n const { owner, repo } = parseRepoString(repoString);\r\n\r\n const { data } = await octokit.rest.pulls.get({\r\n owner,\r\n repo,\r\n pull_number: prNumber,\r\n });\r\n\r\n return mapPullRequest(data);\r\n}\r\n\r\n/**\r\n * Create a new pull request\r\n */\r\nexport async function createPullRequest(\r\n repoString: string,\r\n options: CreatePullRequestOptions,\r\n config?: GitHubClientConfig\r\n): Promise<PullRequest> {\r\n const octokit = getOctokit(config);\r\n const { owner, repo } = parseRepoString(repoString);\r\n\r\n const { data } = await octokit.rest.pulls.create({\r\n owner,\r\n repo,\r\n title: options.title,\r\n body: options.body,\r\n head: options.head,\r\n base: options.base,\r\n draft: options.draft,\r\n maintainer_can_modify: options.maintainerCanModify,\r\n });\r\n\r\n return mapPullRequest(data);\r\n}\r\n\r\n/**\r\n * Update a pull request\r\n */\r\nexport async function updatePullRequest(\r\n repoString: string,\r\n prNumber: number,\r\n options: UpdatePullRequestOptions,\r\n config?: GitHubClientConfig\r\n): Promise<PullRequest> {\r\n const octokit = getOctokit(config);\r\n const { owner, repo } = parseRepoString(repoString);\r\n\r\n const { data } = await octokit.rest.pulls.update({\r\n owner,\r\n repo,\r\n pull_number: prNumber,\r\n title: options.title,\r\n body: options.body,\r\n state: options.state,\r\n base: options.base,\r\n maintainer_can_modify: options.maintainerCanModify,\r\n });\r\n\r\n return mapPullRequest(data);\r\n}\r\n\r\n/**\r\n * Close a pull request without merging\r\n */\r\nexport async function closePullRequest(\r\n repoString: string,\r\n prNumber: number,\r\n config?: GitHubClientConfig\r\n): Promise<PullRequest> {\r\n return updatePullRequest(repoString, prNumber, { state: \"closed\" }, config);\r\n}\r\n\r\n/**\r\n * Reopen a closed pull request\r\n */\r\nexport async function reopenPullRequest(\r\n repoString: string,\r\n prNumber: number,\r\n config?: GitHubClientConfig\r\n): Promise<PullRequest> {\r\n return updatePullRequest(repoString, prNumber, { state: \"open\" }, config);\r\n}\r\n\r\n/**\r\n * Mark a pull request as ready for review (remove draft status)\r\n */\r\nexport async function markReadyForReview(\r\n repoString: string,\r\n prNumber: number,\r\n config?: GitHubClientConfig\r\n): Promise<void> {\r\n const octokit = getOctokit(config);\r\n const { owner, repo } = parseRepoString(repoString);\r\n\r\n // Use GraphQL API for this as REST doesn't support it\r\n await octokit.graphql(`\r\n mutation($pullRequestId: ID!) {\r\n markPullRequestReadyForReview(input: {pullRequestId: $pullRequestId}) {\r\n pullRequest {\r\n id\r\n }\r\n }\r\n }\r\n `, {\r\n pullRequestId: `PR_${Buffer.from(`010:PullRequest${await getPullRequestNodeId(octokit, owner, repo, prNumber)}`).toString(\"base64\")}`,\r\n }).catch(async () => {\r\n // Fallback: Get node ID properly\r\n const pr = await getPullRequest(repoString, prNumber, config);\r\n // If fallback is needed, the PR might already be ready or we don't have permission\r\n console.log(\"PR may already be ready for review or permissions are insufficient\");\r\n });\r\n}\r\n\r\n/**\r\n * Convert a pull request to draft\r\n */\r\nexport async function convertToDraft(\r\n repoString: string,\r\n prNumber: number,\r\n config?: GitHubClientConfig\r\n): Promise<void> {\r\n const octokit = getOctokit(config);\r\n const { owner, repo } = parseRepoString(repoString);\r\n\r\n // This requires GraphQL API\r\n const { repository } = await octokit.graphql<{ repository: { pullRequest: { id: string } } }>(`\r\n query($owner: String!, $repo: String!, $number: Int!) {\r\n repository(owner: $owner, name: $repo) {\r\n pullRequest(number: $number) {\r\n id\r\n }\r\n }\r\n }\r\n `, { owner, repo, number: prNumber });\r\n\r\n await octokit.graphql(`\r\n mutation($pullRequestId: ID!) {\r\n convertPullRequestToDraft(input: {pullRequestId: $pullRequestId}) {\r\n pullRequest {\r\n id\r\n }\r\n }\r\n }\r\n `, { pullRequestId: repository.pullRequest.id });\r\n}\r\n\r\n/**\r\n * Request reviewers for a pull request\r\n */\r\nexport async function requestReviewers(\r\n repoString: string,\r\n prNumber: number,\r\n reviewers: string[],\r\n teamReviewers?: string[],\r\n config?: GitHubClientConfig\r\n): Promise<PullRequest> {\r\n const octokit = getOctokit(config);\r\n const { owner, repo } = parseRepoString(repoString);\r\n\r\n const { data } = await octokit.rest.pulls.requestReviewers({\r\n owner,\r\n repo,\r\n pull_number: prNumber,\r\n reviewers,\r\n team_reviewers: teamReviewers,\r\n });\r\n\r\n return mapPullRequest(data);\r\n}\r\n\r\n/**\r\n * Remove review request\r\n */\r\nexport async function removeReviewRequest(\r\n repoString: string,\r\n prNumber: number,\r\n reviewers: string[],\r\n teamReviewers?: string[],\r\n config?: GitHubClientConfig\r\n): Promise<PullRequest> {\r\n const octokit = getOctokit(config);\r\n const { owner, repo } = parseRepoString(repoString);\r\n\r\n const { data } = await octokit.rest.pulls.removeRequestedReviewers({\r\n owner,\r\n repo,\r\n pull_number: prNumber,\r\n reviewers,\r\n team_reviewers: teamReviewers,\r\n });\r\n\r\n return mapPullRequest(data);\r\n}\r\n\r\n/**\r\n * List reviews on a pull request\r\n */\r\nexport async function listReviews(\r\n repoString: string,\r\n prNumber: number,\r\n options?: { perPage?: number; page?: number },\r\n config?: GitHubClientConfig\r\n): Promise<PullRequestReview[]> {\r\n const octokit = getOctokit(config);\r\n const { owner, repo } = parseRepoString(repoString);\r\n\r\n const { data } = await octokit.rest.pulls.listReviews({\r\n owner,\r\n repo,\r\n pull_number: prNumber,\r\n per_page: options?.perPage || 30,\r\n page: options?.page || 1,\r\n });\r\n\r\n return data.map(mapReview);\r\n}\r\n\r\n/**\r\n * Create a review on a pull request\r\n */\r\nexport async function createReview(\r\n repoString: string,\r\n prNumber: number,\r\n options: CreateReviewOptions,\r\n config?: GitHubClientConfig\r\n): Promise<PullRequestReview> {\r\n const octokit = getOctokit(config);\r\n const { owner, repo } = parseRepoString(repoString);\r\n\r\n const { data } = await octokit.rest.pulls.createReview({\r\n owner,\r\n repo,\r\n pull_number: prNumber,\r\n commit_id: options.commitId,\r\n body: options.body,\r\n event: options.event,\r\n comments: options.comments,\r\n });\r\n\r\n return mapReview(data);\r\n}\r\n\r\n/**\r\n * Approve a pull request\r\n */\r\nexport async function approvePullRequest(\r\n repoString: string,\r\n prNumber: number,\r\n body?: string,\r\n config?: GitHubClientConfig\r\n): Promise<PullRequestReview> {\r\n return createReview(repoString, prNumber, {\r\n event: \"APPROVE\",\r\n body,\r\n }, config);\r\n}\r\n\r\n/**\r\n * Request changes on a pull request\r\n */\r\nexport async function requestChanges(\r\n repoString: string,\r\n prNumber: number,\r\n body: string,\r\n config?: GitHubClientConfig\r\n): Promise<PullRequestReview> {\r\n return createReview(repoString, prNumber, {\r\n event: \"REQUEST_CHANGES\",\r\n body,\r\n }, config);\r\n}\r\n\r\n/**\r\n * Submit a pending review\r\n */\r\nexport async function submitReview(\r\n repoString: string,\r\n prNumber: number,\r\n reviewId: number,\r\n event: \"APPROVE\" | \"REQUEST_CHANGES\" | \"COMMENT\",\r\n body?: string,\r\n config?: GitHubClientConfig\r\n): Promise<PullRequestReview> {\r\n const octokit = getOctokit(config);\r\n const { owner, repo } = parseRepoString(repoString);\r\n\r\n const { data } = await octokit.rest.pulls.submitReview({\r\n owner,\r\n repo,\r\n pull_number: prNumber,\r\n review_id: reviewId,\r\n event,\r\n body,\r\n });\r\n\r\n return mapReview(data);\r\n}\r\n\r\n/**\r\n * Dismiss a review\r\n */\r\nexport async function dismissReview(\r\n repoString: string,\r\n prNumber: number,\r\n reviewId: number,\r\n message: string,\r\n config?: GitHubClientConfig\r\n): Promise<PullRequestReview> {\r\n const octokit = getOctokit(config);\r\n const { owner, repo } = parseRepoString(repoString);\r\n\r\n const { data } = await octokit.rest.pulls.dismissReview({\r\n owner,\r\n repo,\r\n pull_number: prNumber,\r\n review_id: reviewId,\r\n message,\r\n });\r\n\r\n return mapReview(data);\r\n}\r\n\r\n/**\r\n * List review comments on a pull request\r\n */\r\nexport async function listReviewComments(\r\n repoString: string,\r\n prNumber: number,\r\n options?: { sort?: \"created\" | \"updated\"; direction?: \"asc\" | \"desc\"; since?: string; perPage?: number; page?: number },\r\n config?: GitHubClientConfig\r\n): Promise<ReviewComment[]> {\r\n const octokit = getOctokit(config);\r\n const { owner, repo } = parseRepoString(repoString);\r\n\r\n const { data } = await octokit.rest.pulls.listReviewComments({\r\n owner,\r\n repo,\r\n pull_number: prNumber,\r\n sort: options?.sort,\r\n direction: options?.direction,\r\n since: options?.since,\r\n per_page: options?.perPage || 30,\r\n page: options?.page || 1,\r\n });\r\n\r\n return data.map(mapReviewComment);\r\n}\r\n\r\n/**\r\n * Create a review comment\r\n */\r\nexport async function createReviewComment(\r\n repoString: string,\r\n prNumber: number,\r\n body: string,\r\n commitId: string,\r\n path: string,\r\n options?: { position?: number; line?: number; side?: \"LEFT\" | \"RIGHT\"; startLine?: number; startSide?: \"LEFT\" | \"RIGHT\"; inReplyTo?: number },\r\n config?: GitHubClientConfig\r\n): Promise<ReviewComment> {\r\n const octokit = getOctokit(config);\r\n const { owner, repo } = parseRepoString(repoString);\r\n\r\n const { data } = await octokit.rest.pulls.createReviewComment({\r\n owner,\r\n repo,\r\n pull_number: prNumber,\r\n body,\r\n commit_id: commitId,\r\n path,\r\n position: options?.position,\r\n line: options?.line,\r\n side: options?.side,\r\n start_line: options?.startLine,\r\n start_side: options?.startSide,\r\n in_reply_to: options?.inReplyTo,\r\n });\r\n\r\n return mapReviewComment(data);\r\n}\r\n\r\n/**\r\n * Reply to a review comment\r\n */\r\nexport async function replyToReviewComment(\r\n repoString: string,\r\n prNumber: number,\r\n commentId: number,\r\n body: string,\r\n config?: GitHubClientConfig\r\n): Promise<ReviewComment> {\r\n const octokit = getOctokit(config);\r\n const { owner, repo } = parseRepoString(repoString);\r\n\r\n const { data } = await octokit.rest.pulls.createReplyForReviewComment({\r\n owner,\r\n repo,\r\n pull_number: prNumber,\r\n comment_id: commentId,\r\n body,\r\n });\r\n\r\n return mapReviewComment(data);\r\n}\r\n\r\n/**\r\n * Get files changed in a pull request\r\n */\r\nexport async function listFiles(\r\n repoString: string,\r\n prNumber: number,\r\n options?: { perPage?: number; page?: number },\r\n config?: GitHubClientConfig\r\n): Promise<PullRequestFile[]> {\r\n const octokit = getOctokit(config);\r\n const { owner, repo } = parseRepoString(repoString);\r\n\r\n const { data } = await octokit.rest.pulls.listFiles({\r\n owner,\r\n repo,\r\n pull_number: prNumber,\r\n per_page: options?.perPage || 30,\r\n page: options?.page || 1,\r\n });\r\n\r\n return data.map(mapPullRequestFile);\r\n}\r\n\r\n/**\r\n * Get commits in a pull request\r\n */\r\nexport async function listCommits(\r\n repoString: string,\r\n prNumber: number,\r\n options?: { perPage?: number; page?: number },\r\n config?: GitHubClientConfig\r\n): Promise<Array<{ sha: string; message: string; author: { name: string; email: string; date: string } | null; committer: { name: string; email: string; date: string } | null; htmlUrl: string }>> {\r\n const octokit = getOctokit(config);\r\n const { owner, repo } = parseRepoString(repoString);\r\n\r\n const { data } = await octokit.rest.pulls.listCommits({\r\n owner,\r\n repo,\r\n pull_number: prNumber,\r\n per_page: options?.perPage || 30,\r\n page: options?.page || 1,\r\n });\r\n\r\n return data.map((commit: any) => ({\r\n sha: commit.sha,\r\n message: commit.commit.message,\r\n author: commit.commit.author\r\n ? {\r\n name: commit.commit.author.name || \"\",\r\n email: commit.commit.author.email || \"\",\r\n date: commit.commit.author.date || \"\",\r\n }\r\n : null,\r\n committer: commit.commit.committer\r\n ? {\r\n name: commit.commit.committer.name || \"\",\r\n email: commit.commit.committer.email || \"\",\r\n date: commit.commit.committer.date || \"\",\r\n }\r\n : null,\r\n htmlUrl: commit.html_url,\r\n }));\r\n}\r\n\r\n/**\r\n * Check if a pull request can be merged\r\n */\r\nexport async function checkMergeability(\r\n repoString: string,\r\n prNumber: number,\r\n config?: GitHubClientConfig\r\n): Promise<{ mergeable: boolean | null; mergeableState: string; merged: boolean }> {\r\n const pr = await getPullRequest(repoString, prNumber, config);\r\n\r\n return {\r\n mergeable: pr.mergeable,\r\n mergeableState: pr.mergeableState,\r\n merged: pr.merged,\r\n };\r\n}\r\n\r\n/**\r\n * Merge a pull request\r\n */\r\nexport async function mergePullRequest(\r\n repoString: string,\r\n prNumber: number,\r\n options: MergeOptions = {},\r\n config?: GitHubClientConfig\r\n): Promise<{ sha: string; merged: boolean; message: string }> {\r\n const octokit = getOctokit(config);\r\n const { owner, repo } = parseRepoString(repoString);\r\n\r\n const { data } = await octokit.rest.pulls.merge({\r\n owner,\r\n repo,\r\n pull_number: prNumber,\r\n commit_title: options.commitTitle,\r\n commit_message: options.commitMessage,\r\n sha: options.sha,\r\n merge_method: options.mergeMethod || \"merge\",\r\n });\r\n\r\n return {\r\n sha: data.sha,\r\n merged: data.merged,\r\n message: data.message,\r\n };\r\n}\r\n\r\n/**\r\n * Update a pull request branch (merge base into head)\r\n */\r\nexport async function updateBranch(\r\n repoString: string,\r\n prNumber: number,\r\n expectedHeadSha?: string,\r\n config?: GitHubClientConfig\r\n): Promise<{ message: string; url: string }> {\r\n const octokit = getOctokit(config);\r\n const { owner, repo } = parseRepoString(repoString);\r\n\r\n const { data } = await octokit.rest.pulls.updateBranch({\r\n owner,\r\n repo,\r\n pull_number: prNumber,\r\n expected_head_sha: expectedHeadSha,\r\n });\r\n\r\n return {\r\n message: data.message || \"\",\r\n url: data.url || \"\",\r\n };\r\n}\r\n\r\n// Helper to get PR node ID for GraphQL operations\r\nasync function getPullRequestNodeId(\r\n octokit: ReturnType<typeof getOctokit>,\r\n owner: string,\r\n repo: string,\r\n prNumber: number\r\n): Promise<string> {\r\n const { repository } = await octokit.graphql<{ repository: { pullRequest: { id: string } } }>(`\r\n query($owner: String!, $repo: String!, $number: Int!) {\r\n repository(owner: $owner, name: $repo) {\r\n pullRequest(number: $number) {\r\n id\r\n }\r\n }\r\n }\r\n `, { owner, repo, number: prNumber });\r\n\r\n return repository.pullRequest.id;\r\n}\r\n\r\n// Helper function to map API response to PullRequest interface\r\nfunction mapPullRequest(data: any): PullRequest {\r\n return {\r\n id: data.id,\r\n number: data.number,\r\n title: data.title,\r\n body: data.body,\r\n state: data.state,\r\n htmlUrl: data.html_url,\r\n diffUrl: data.diff_url,\r\n patchUrl: data.patch_url,\r\n draft: data.draft || false,\r\n merged: data.merged || false,\r\n mergeable: data.mergeable,\r\n mergeableState: data.mergeable_state || \"unknown\",\r\n mergedAt: data.merged_at,\r\n mergedBy: data.merged_by\r\n ? {\r\n login: data.merged_by.login,\r\n id: data.merged_by.id,\r\n avatarUrl: data.merged_by.avatar_url,\r\n }\r\n : null,\r\n user: data.user\r\n ? {\r\n login: data.user.login,\r\n id: data.user.id,\r\n avatarUrl: data.user.avatar_url,\r\n }\r\n : null,\r\n head: {\r\n ref: data.head.ref,\r\n sha: data.head.sha,\r\n repo: data.head.repo\r\n ? {\r\n fullName: data.head.repo.full_name,\r\n cloneUrl: data.head.repo.clone_url,\r\n }\r\n : null,\r\n },\r\n base: {\r\n ref: data.base.ref,\r\n sha: data.base.sha,\r\n repo: data.base.repo\r\n ? {\r\n fullName: data.base.repo.full_name,\r\n cloneUrl: data.base.repo.clone_url,\r\n }\r\n : null,\r\n },\r\n labels: data.labels?.map((label: any) => ({\r\n id: label.id,\r\n name: label.name,\r\n color: label.color,\r\n description: label.description,\r\n })) || [],\r\n assignees: data.assignees?.map((assignee: any) => ({\r\n login: assignee.login,\r\n id: assignee.id,\r\n avatarUrl: assignee.avatar_url,\r\n })) || [],\r\n requestedReviewers: data.requested_reviewers?.map((reviewer: any) => ({\r\n login: reviewer.login,\r\n id: reviewer.id,\r\n avatarUrl: reviewer.avatar_url,\r\n })) || [],\r\n requestedTeams: data.requested_teams?.map((team: any) => ({\r\n id: team.id,\r\n name: team.name,\r\n slug: team.slug,\r\n })) || [],\r\n milestone: data.milestone\r\n ? {\r\n id: data.milestone.id,\r\n number: data.milestone.number,\r\n title: data.milestone.title,\r\n state: data.milestone.state,\r\n }\r\n : null,\r\n additions: data.additions || 0,\r\n deletions: data.deletions || 0,\r\n changedFiles: data.changed_files || 0,\r\n commits: data.commits || 0,\r\n comments: data.comments || 0,\r\n reviewComments: data.review_comments || 0,\r\n createdAt: data.created_at,\r\n updatedAt: data.updated_at,\r\n closedAt: data.closed_at,\r\n };\r\n}\r\n\r\n// Helper function to map API response to PullRequestReview interface\r\nfunction mapReview(data: any): PullRequestReview {\r\n return {\r\n id: data.id,\r\n user: data.user\r\n ? {\r\n login: data.user.login,\r\n id: data.user.id,\r\n avatarUrl: data.user.avatar_url,\r\n }\r\n : null,\r\n body: data.body,\r\n state: data.state,\r\n htmlUrl: data.html_url,\r\n submittedAt: data.submitted_at,\r\n commitId: data.commit_id,\r\n };\r\n}\r\n\r\n// Helper function to map API response to ReviewComment interface\r\nfunction mapReviewComment(data: any): ReviewComment {\r\n return {\r\n id: data.id,\r\n pullRequestReviewId: data.pull_request_review_id,\r\n diffHunk: data.diff_hunk,\r\n path: data.path,\r\n position: data.position,\r\n originalPosition: data.original_position,\r\n commitId: data.commit_id,\r\n originalCommitId: data.original_commit_id,\r\n user: data.user\r\n ? {\r\n login: data.user.login,\r\n id: data.user.id,\r\n avatarUrl: data.user.avatar_url,\r\n }\r\n : null,\r\n body: data.body,\r\n htmlUrl: data.html_url,\r\n createdAt: data.created_at,\r\n updatedAt: data.updated_at,\r\n line: data.line,\r\n side: data.side || \"RIGHT\",\r\n startLine: data.start_line,\r\n startSide: data.start_side,\r\n inReplyToId: data.in_reply_to_id,\r\n };\r\n}\r\n\r\n// Helper function to map API response to PullRequestFile interface\r\nfunction mapPullRequestFile(data: any): PullRequestFile {\r\n return {\r\n sha: data.sha,\r\n filename: data.filename,\r\n status: data.status,\r\n additions: data.additions,\r\n deletions: data.deletions,\r\n changes: data.changes,\r\n blobUrl: data.blob_url,\r\n rawUrl: data.raw_url,\r\n contentsUrl: data.contents_url,\r\n patch: data.patch,\r\n previousFilename: data.previous_filename,\r\n };\r\n}\r\n","/**\r\n * AI-Powered Code Review\r\n *\r\n * Uses the configured LLM provider to provide intelligent code review on pull requests.\r\n */\r\n\r\nimport { providerRegistry } from \"../../core/providers\";\r\nimport { getOctokit, parseRepoString, type GitHubClientConfig } from \"./client\";\r\nimport { getPullRequest, listFiles, listCommits, createReview, type PullRequestFile, type CreateReviewOptions } from \"./pull-requests\";\r\nimport { getContents } from \"./repos\";\r\n\r\nexport interface CodeReviewOptions {\r\n /**\r\n * Focus areas for the review\r\n */\r\n focusAreas?: Array<\r\n | \"security\"\r\n | \"performance\"\r\n | \"maintainability\"\r\n | \"readability\"\r\n | \"testing\"\r\n | \"documentation\"\r\n | \"error-handling\"\r\n | \"best-practices\"\r\n >;\r\n\r\n /**\r\n * Language-specific guidelines to apply\r\n */\r\n language?: string;\r\n\r\n /**\r\n * Custom review guidelines or rules\r\n */\r\n customGuidelines?: string;\r\n\r\n /**\r\n * Maximum number of files to review (default: 20)\r\n */\r\n maxFiles?: number;\r\n\r\n /**\r\n * Whether to automatically submit the review\r\n */\r\n autoSubmit?: boolean;\r\n\r\n /**\r\n * Severity threshold for auto-approval\r\n * If all issues are below this severity, approve automatically\r\n */\r\n autoApproveThreshold?: \"info\" | \"warning\" | \"error\";\r\n\r\n /**\r\n * GitHub client configuration\r\n */\r\n githubConfig?: GitHubClientConfig;\r\n}\r\n\r\nexport interface ReviewIssue {\r\n severity: \"info\" | \"warning\" | \"error\";\r\n file: string;\r\n line?: number;\r\n endLine?: number;\r\n message: string;\r\n suggestion?: string;\r\n category: string;\r\n}\r\n\r\nexport interface CodeReviewResult {\r\n pullRequest: {\r\n number: number;\r\n title: string;\r\n author: string | null;\r\n url: string;\r\n };\r\n summary: string;\r\n issues: ReviewIssue[];\r\n filesReviewed: number;\r\n linesReviewed: number;\r\n overallAssessment: \"approve\" | \"request-changes\" | \"comment\";\r\n recommendations: string[];\r\n metrics: {\r\n securityScore: number;\r\n maintainabilityScore: number;\r\n readabilityScore: number;\r\n overallScore: number;\r\n };\r\n reviewSubmitted: boolean;\r\n reviewId?: number;\r\n}\r\n\r\nexport interface DiffContext {\r\n file: PullRequestFile;\r\n oldContent?: string;\r\n newContent?: string;\r\n patch?: string;\r\n}\r\n\r\n/**\r\n * Perform an AI-powered code review on a pull request\r\n */\r\nexport async function reviewPullRequest(\r\n repoString: string,\r\n prNumber: number,\r\n options: CodeReviewOptions = {}\r\n): Promise<CodeReviewResult> {\r\n const { owner, repo } = parseRepoString(repoString);\r\n const maxFiles = options.maxFiles || 20;\r\n\r\n // Get PR details and files\r\n const [pr, files, commits] = await Promise.all([\r\n getPullRequest(repoString, prNumber, options.githubConfig),\r\n listFiles(repoString, prNumber, { perPage: maxFiles }, options.githubConfig),\r\n listCommits(repoString, prNumber, { perPage: 10 }, options.githubConfig),\r\n ]);\r\n\r\n // Filter out binary files and very large files\r\n const reviewableFiles = files.filter((f) => {\r\n // Skip binary files (no patch available)\r\n if (!f.patch) return false;\r\n // Skip very large patches\r\n if (f.patch.length > 50000) return false;\r\n // Skip generated files\r\n if (isGeneratedFile(f.filename)) return false;\r\n return true;\r\n });\r\n\r\n // Build context for the review\r\n const diffContexts: DiffContext[] = reviewableFiles.map((file) => ({\r\n file,\r\n patch: file.patch,\r\n }));\r\n\r\n // Calculate total lines\r\n const totalLines = reviewableFiles.reduce(\r\n (sum, f) => sum + f.additions + f.deletions,\r\n 0\r\n );\r\n\r\n // Build the review prompt\r\n const prompt = buildReviewPrompt(pr, diffContexts, commits, options);\r\n\r\n // Get AI review using the configured provider\r\n const provider = providerRegistry.getDefault();\r\n const response = await provider.createMessage({\r\n model: \"claude-sonnet-4-20250514\",\r\n max_tokens: 8192,\r\n system: getSystemPrompt(options),\r\n messages: [{ role: \"user\", content: prompt }],\r\n });\r\n\r\n // Parse the review response\r\n const reviewContent =\r\n response.content[0]?.type === \"text\" ? response.content[0].text || \"\" : \"\";\r\n\r\n const reviewResult = parseReviewResponse(\r\n reviewContent,\r\n pr,\r\n reviewableFiles.length,\r\n totalLines\r\n );\r\n\r\n // Submit the review if requested\r\n if (options.autoSubmit) {\r\n const reviewOptions = buildReviewOptions(reviewResult);\r\n const review = await createReview(\r\n repoString,\r\n prNumber,\r\n reviewOptions,\r\n options.githubConfig\r\n );\r\n reviewResult.reviewSubmitted = true;\r\n reviewResult.reviewId = review.id;\r\n }\r\n\r\n return reviewResult;\r\n}\r\n\r\n/**\r\n * Review a specific file in a pull request\r\n */\r\nexport async function reviewFile(\r\n repoString: string,\r\n prNumber: number,\r\n filename: string,\r\n options: CodeReviewOptions = {}\r\n): Promise<{\r\n issues: ReviewIssue[];\r\n suggestions: string[];\r\n summary: string;\r\n}> {\r\n const files = await listFiles(repoString, prNumber, { perPage: 100 }, options.githubConfig);\r\n const file = files.find((f) => f.filename === filename);\r\n\r\n if (!file) {\r\n throw new Error(`File not found in pull request: ${filename}`);\r\n }\r\n\r\n if (!file.patch) {\r\n throw new Error(`No diff available for file: ${filename}`);\r\n }\r\n\r\n const prompt = buildSingleFileReviewPrompt(file, options);\r\n\r\n const provider = providerRegistry.getDefault();\r\n const response = await provider.createMessage({\r\n model: \"claude-sonnet-4-20250514\",\r\n max_tokens: 4096,\r\n system: getSystemPrompt(options),\r\n messages: [{ role: \"user\", content: prompt }],\r\n });\r\n\r\n const reviewContent =\r\n response.content[0]?.type === \"text\" ? response.content[0].text || \"\" : \"\";\r\n\r\n return parseSingleFileReview(reviewContent, filename);\r\n}\r\n\r\n/**\r\n * Generate a summary of changes in a pull request\r\n */\r\nexport async function summarizeChanges(\r\n repoString: string,\r\n prNumber: number,\r\n options?: { githubConfig?: GitHubClientConfig }\r\n): Promise<{\r\n summary: string;\r\n keyChanges: string[];\r\n impactAreas: string[];\r\n breakingChanges: string[];\r\n testingRecommendations: string[];\r\n}> {\r\n const [pr, files, commits] = await Promise.all([\r\n getPullRequest(repoString, prNumber, options?.githubConfig),\r\n listFiles(repoString, prNumber, { perPage: 50 }, options?.githubConfig),\r\n listCommits(repoString, prNumber, { perPage: 20 }, options?.githubConfig),\r\n ]);\r\n\r\n const prompt = `Analyze this pull request and provide a summary:\r\n\r\n**Pull Request:** ${pr.title}\r\n**Description:** ${pr.body || \"No description provided\"}\r\n**Author:** ${pr.user?.login || \"Unknown\"}\r\n**Commits:** ${commits.length}\r\n**Files Changed:** ${files.length}\r\n**Lines Changed:** +${pr.additions} / -${pr.deletions}\r\n\r\n**Commit Messages:**\r\n${commits.map((c) => `- ${c.message.split(\"\\n\")[0]}`).join(\"\\n\")}\r\n\r\n**Files Changed:**\r\n${files.map((f) => `- ${f.filename} (${f.status}: +${f.additions}/-${f.deletions})`).join(\"\\n\")}\r\n\r\nProvide your analysis in the following JSON format:\r\n{\r\n \"summary\": \"A concise 2-3 sentence summary of what this PR does\",\r\n \"keyChanges\": [\"Key change 1\", \"Key change 2\"],\r\n \"impactAreas\": [\"Area that might be affected\"],\r\n \"breakingChanges\": [\"Any breaking changes, or empty array if none\"],\r\n \"testingRecommendations\": [\"What should be tested\"]\r\n}`;\r\n\r\n const provider = providerRegistry.getDefault();\r\n const response = await provider.createMessage({\r\n model: \"claude-sonnet-4-20250514\",\r\n max_tokens: 2048,\r\n messages: [{ role: \"user\", content: prompt }],\r\n });\r\n\r\n const content =\r\n response.content[0]?.type === \"text\" ? response.content[0].text || \"\" : \"\";\r\n\r\n try {\r\n // Extract JSON from the response\r\n const jsonMatch = content.match(/\\{[\\s\\S]*\\}/);\r\n if (jsonMatch) {\r\n return JSON.parse(jsonMatch[0]);\r\n }\r\n } catch (e) {\r\n // Fall back to default structure\r\n }\r\n\r\n return {\r\n summary: content,\r\n keyChanges: [],\r\n impactAreas: [],\r\n breakingChanges: [],\r\n testingRecommendations: [],\r\n };\r\n}\r\n\r\n/**\r\n * Check for security issues in a pull request\r\n */\r\nexport async function securityScan(\r\n repoString: string,\r\n prNumber: number,\r\n options?: { githubConfig?: GitHubClientConfig }\r\n): Promise<{\r\n vulnerabilities: Array<{\r\n severity: \"low\" | \"medium\" | \"high\" | \"critical\";\r\n type: string;\r\n file: string;\r\n line?: number;\r\n description: string;\r\n recommendation: string;\r\n }>;\r\n securityScore: number;\r\n summary: string;\r\n}> {\r\n const files = await listFiles(repoString, prNumber, { perPage: 50 }, options?.githubConfig);\r\n\r\n const reviewableFiles = files.filter((f) => f.patch && !isGeneratedFile(f.filename));\r\n\r\n const prompt = `Perform a security analysis of these code changes.\r\n\r\n**Files Changed:**\r\n${reviewableFiles\r\n .map(\r\n (f) => `\r\n### ${f.filename}\r\n\\`\\`\\`diff\r\n${f.patch?.slice(0, 5000) || \"\"}\r\n\\`\\`\\`\r\n`\r\n )\r\n .join(\"\\n\")}\r\n\r\nLook for:\r\n1. SQL injection vulnerabilities\r\n2. XSS vulnerabilities\r\n3. Authentication/authorization issues\r\n4. Hardcoded secrets or credentials\r\n5. Insecure dependencies\r\n6. Path traversal vulnerabilities\r\n7. Command injection\r\n8. Insecure deserialization\r\n9. Sensitive data exposure\r\n10. Security misconfigurations\r\n\r\nRespond in JSON format:\r\n{\r\n \"vulnerabilities\": [\r\n {\r\n \"severity\": \"high\",\r\n \"type\": \"SQL Injection\",\r\n \"file\": \"path/to/file.ts\",\r\n \"line\": 42,\r\n \"description\": \"Description of the issue\",\r\n \"recommendation\": \"How to fix it\"\r\n }\r\n ],\r\n \"securityScore\": 85,\r\n \"summary\": \"Overall security assessment\"\r\n}`;\r\n\r\n const provider = providerRegistry.getDefault();\r\n const response = await provider.createMessage({\r\n model: \"claude-sonnet-4-20250514\",\r\n max_tokens: 4096,\r\n system: \"You are a security expert reviewing code for vulnerabilities. Be thorough but avoid false positives.\",\r\n messages: [{ role: \"user\", content: prompt }],\r\n });\r\n\r\n const content =\r\n response.content[0]?.type === \"text\" ? response.content[0].text || \"\" : \"\";\r\n\r\n try {\r\n const jsonMatch = content.match(/\\{[\\s\\S]*\\}/);\r\n if (jsonMatch) {\r\n return JSON.parse(jsonMatch[0]);\r\n }\r\n } catch (e) {\r\n // Fall back to default\r\n }\r\n\r\n return {\r\n vulnerabilities: [],\r\n securityScore: 100,\r\n summary: \"Unable to parse security scan results\",\r\n };\r\n}\r\n\r\n// Helper functions\r\n\r\nfunction getSystemPrompt(options: CodeReviewOptions): string {\r\n let prompt = `You are an expert code reviewer with deep knowledge of software engineering best practices.\r\nYour task is to review pull requests and provide constructive, actionable feedback.\r\n\r\nGuidelines:\r\n- Be specific and constructive in your feedback\r\n- Prioritize issues by severity (error > warning > info)\r\n- Include code suggestions when possible\r\n- Consider the context and purpose of the changes\r\n- Acknowledge good practices when you see them`;\r\n\r\n if (options.focusAreas?.length) {\r\n prompt += `\\n\\nFocus especially on: ${options.focusAreas.join(\", \")}`;\r\n }\r\n\r\n if (options.language) {\r\n prompt += `\\n\\nThis codebase uses ${options.language}. Apply language-specific best practices.`;\r\n }\r\n\r\n if (options.customGuidelines) {\r\n prompt += `\\n\\nCustom Review Guidelines:\\n${options.customGuidelines}`;\r\n }\r\n\r\n return prompt;\r\n}\r\n\r\nfunction buildReviewPrompt(\r\n pr: Awaited<ReturnType<typeof getPullRequest>>,\r\n contexts: DiffContext[],\r\n commits: Awaited<ReturnType<typeof listCommits>>,\r\n options: CodeReviewOptions\r\n): string {\r\n let prompt = `Review this pull request:\r\n\r\n**Title:** ${pr.title}\r\n**Author:** ${pr.user?.login || \"Unknown\"}\r\n**Description:**\r\n${pr.body || \"No description provided\"}\r\n\r\n**Commits (${commits.length}):**\r\n${commits.slice(0, 5).map((c) => `- ${c.message.split(\"\\n\")[0]}`).join(\"\\n\")}\r\n${commits.length > 5 ? `... and ${commits.length - 5} more commits` : \"\"}\r\n\r\n**Changes:**\r\n- Files changed: ${contexts.length}\r\n- Additions: ${pr.additions}\r\n- Deletions: ${pr.deletions}\r\n\r\n**File Diffs:**\r\n`;\r\n\r\n for (const ctx of contexts) {\r\n prompt += `\r\n### ${ctx.file.filename} (${ctx.file.status}: +${ctx.file.additions}/-${ctx.file.deletions})\r\n\\`\\`\\`diff\r\n${ctx.patch?.slice(0, 8000) || \"No diff available\"}\r\n\\`\\`\\`\r\n`;\r\n }\r\n\r\n prompt += `\r\n\r\nPlease provide your review in the following JSON format:\r\n{\r\n \"summary\": \"A brief summary of the overall changes and their quality\",\r\n \"issues\": [\r\n {\r\n \"severity\": \"error|warning|info\",\r\n \"file\": \"path/to/file.ts\",\r\n \"line\": 42,\r\n \"endLine\": 45,\r\n \"message\": \"Description of the issue\",\r\n \"suggestion\": \"Code or explanation of how to fix it\",\r\n \"category\": \"security|performance|maintainability|readability|testing|documentation|error-handling|best-practices\"\r\n }\r\n ],\r\n \"overallAssessment\": \"approve|request-changes|comment\",\r\n \"recommendations\": [\"General recommendation 1\", \"General recommendation 2\"],\r\n \"metrics\": {\r\n \"securityScore\": 85,\r\n \"maintainabilityScore\": 90,\r\n \"readabilityScore\": 88,\r\n \"overallScore\": 87\r\n }\r\n}`;\r\n\r\n return prompt;\r\n}\r\n\r\nfunction buildSingleFileReviewPrompt(\r\n file: PullRequestFile,\r\n options: CodeReviewOptions\r\n): string {\r\n return `Review this file change:\r\n\r\n**File:** ${file.filename}\r\n**Status:** ${file.status}\r\n**Changes:** +${file.additions}/-${file.deletions}\r\n\r\n\\`\\`\\`diff\r\n${file.patch || \"No diff available\"}\r\n\\`\\`\\`\r\n\r\nProvide your review in JSON format:\r\n{\r\n \"issues\": [\r\n {\r\n \"severity\": \"error|warning|info\",\r\n \"line\": 42,\r\n \"message\": \"Issue description\",\r\n \"suggestion\": \"How to fix\",\r\n \"category\": \"category\"\r\n }\r\n ],\r\n \"suggestions\": [\"General improvement suggestion\"],\r\n \"summary\": \"Brief summary of the file changes\"\r\n}`;\r\n}\r\n\r\nfunction parseReviewResponse(\r\n content: string,\r\n pr: Awaited<ReturnType<typeof getPullRequest>>,\r\n filesReviewed: number,\r\n linesReviewed: number\r\n): CodeReviewResult {\r\n const defaultResult: CodeReviewResult = {\r\n pullRequest: {\r\n number: pr.number,\r\n title: pr.title,\r\n author: pr.user?.login || null,\r\n url: pr.htmlUrl,\r\n },\r\n summary: \"\",\r\n issues: [],\r\n filesReviewed,\r\n linesReviewed,\r\n overallAssessment: \"comment\",\r\n recommendations: [],\r\n metrics: {\r\n securityScore: 0,\r\n maintainabilityScore: 0,\r\n readabilityScore: 0,\r\n overallScore: 0,\r\n },\r\n reviewSubmitted: false,\r\n };\r\n\r\n try {\r\n const jsonMatch = content.match(/\\{[\\s\\S]*\\}/);\r\n if (jsonMatch) {\r\n const parsed = JSON.parse(jsonMatch[0]);\r\n return {\r\n ...defaultResult,\r\n summary: parsed.summary || \"\",\r\n issues: parsed.issues || [],\r\n overallAssessment: parsed.overallAssessment || \"comment\",\r\n recommendations: parsed.recommendations || [],\r\n metrics: parsed.metrics || defaultResult.metrics,\r\n };\r\n }\r\n } catch (e) {\r\n // JSON parsing failed, return default with content as summary\r\n return {\r\n ...defaultResult,\r\n summary: content.slice(0, 500),\r\n };\r\n }\r\n\r\n return defaultResult;\r\n}\r\n\r\nfunction parseSingleFileReview(\r\n content: string,\r\n filename: string\r\n): {\r\n issues: ReviewIssue[];\r\n suggestions: string[];\r\n summary: string;\r\n} {\r\n const defaultResult = {\r\n issues: [],\r\n suggestions: [],\r\n summary: content.slice(0, 300),\r\n };\r\n\r\n try {\r\n const jsonMatch = content.match(/\\{[\\s\\S]*\\}/);\r\n if (jsonMatch) {\r\n const parsed = JSON.parse(jsonMatch[0]);\r\n return {\r\n issues: (parsed.issues || []).map((issue: any) => ({\r\n ...issue,\r\n file: filename,\r\n })),\r\n suggestions: parsed.suggestions || [],\r\n summary: parsed.summary || \"\",\r\n };\r\n }\r\n } catch (e) {\r\n // Fall back to default\r\n }\r\n\r\n return defaultResult;\r\n}\r\n\r\nfunction buildReviewOptions(result: CodeReviewResult): CreateReviewOptions {\r\n const comments: CreateReviewOptions[\"comments\"] = [];\r\n\r\n // Add inline comments for issues with line numbers\r\n for (const issue of result.issues) {\r\n if (issue.line) {\r\n comments.push({\r\n path: issue.file,\r\n line: issue.line,\r\n body: `**${issue.severity.toUpperCase()}** (${issue.category}): ${issue.message}${\r\n issue.suggestion ? `\\n\\n**Suggestion:** ${issue.suggestion}` : \"\"\r\n }`,\r\n });\r\n }\r\n }\r\n\r\n let event: \"APPROVE\" | \"REQUEST_CHANGES\" | \"COMMENT\";\r\n switch (result.overallAssessment) {\r\n case \"approve\":\r\n event = \"APPROVE\";\r\n break;\r\n case \"request-changes\":\r\n event = \"REQUEST_CHANGES\";\r\n break;\r\n default:\r\n event = \"COMMENT\";\r\n }\r\n\r\n let body = `## AI Code Review\\n\\n${result.summary}\\n\\n`;\r\n\r\n if (result.issues.length > 0) {\r\n body += `### Issues Found (${result.issues.length})\\n\\n`;\r\n\r\n const errorCount = result.issues.filter((i) => i.severity === \"error\").length;\r\n const warningCount = result.issues.filter((i) => i.severity === \"warning\").length;\r\n const infoCount = result.issues.filter((i) => i.severity === \"info\").length;\r\n\r\n body += `- Errors: ${errorCount}\\n- Warnings: ${warningCount}\\n- Info: ${infoCount}\\n\\n`;\r\n }\r\n\r\n if (result.recommendations.length > 0) {\r\n body += `### Recommendations\\n\\n`;\r\n for (const rec of result.recommendations) {\r\n body += `- ${rec}\\n`;\r\n }\r\n body += \"\\n\";\r\n }\r\n\r\n body += `### Metrics\\n\\n`;\r\n body += `| Metric | Score |\\n|--------|-------|\\n`;\r\n body += `| Security | ${result.metrics.securityScore}/100 |\\n`;\r\n body += `| Maintainability | ${result.metrics.maintainabilityScore}/100 |\\n`;\r\n body += `| Readability | ${result.metrics.readabilityScore}/100 |\\n`;\r\n body += `| **Overall** | **${result.metrics.overallScore}/100** |\\n`;\r\n\r\n body += `\\n---\\n*Reviewed by OpenSentinel AI*`;\r\n\r\n return {\r\n event,\r\n body,\r\n comments: comments.length > 0 ? comments : undefined,\r\n };\r\n}\r\n\r\nfunction isGeneratedFile(filename: string): boolean {\r\n const generatedPatterns = [\r\n /\\.min\\.(js|css)$/,\r\n /\\.bundle\\.(js|css)$/,\r\n /package-lock\\.json$/,\r\n /yarn\\.lock$/,\r\n /bun\\.lockb$/,\r\n /\\.d\\.ts$/,\r\n /\\.map$/,\r\n /dist\\//,\r\n /build\\//,\r\n /node_modules\\//,\r\n /vendor\\//,\r\n /generated\\//,\r\n /\\.pb\\.(go|ts|js)$/,\r\n /\\.g\\.(dart|swift|kt)$/,\r\n ];\r\n\r\n return generatedPatterns.some((pattern) => pattern.test(filename));\r\n}\r\n"],"mappings":"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","names":[]}
@@ -0,0 +1,236 @@
1
+ import {
2
+ GraphError,
3
+ stripHtml
4
+ } from "./chunk-PBOCSGNL.js";
5
+ import {
6
+ createLogger
7
+ } from "./chunk-7BNFELEK.js";
8
+
9
+ // src/integrations/m365/sharepoint.ts
10
+ var log = createLogger("m365:sharepoint");
11
+ var GRAPH = "https://graph.microsoft.com/v1.0";
12
+ async function graphJson(accessToken, path, init) {
13
+ const url = path.startsWith("http") ? path : `${GRAPH}${path}`;
14
+ const res = await fetch(url, {
15
+ ...init,
16
+ headers: {
17
+ Authorization: `Bearer ${accessToken}`,
18
+ Accept: "application/json",
19
+ ...init?.headers ?? {}
20
+ }
21
+ });
22
+ if (!res.ok) {
23
+ let body = null;
24
+ try {
25
+ body = await res.json();
26
+ } catch {
27
+ }
28
+ const code = body?.error?.code || String(res.status);
29
+ const message = body?.error?.message || res.statusText;
30
+ log.warn("graph call failed", { path, status: res.status, code });
31
+ throw new GraphError(res.status, code, message);
32
+ }
33
+ if (res.status === 204) return void 0;
34
+ return await res.json();
35
+ }
36
+ async function graphBinary(accessToken, path) {
37
+ const url = path.startsWith("http") ? path : `${GRAPH}${path}`;
38
+ const res = await fetch(url, {
39
+ headers: { Authorization: `Bearer ${accessToken}` },
40
+ redirect: "follow"
41
+ });
42
+ if (!res.ok) {
43
+ throw new GraphError(res.status, String(res.status), res.statusText);
44
+ }
45
+ return res.arrayBuffer();
46
+ }
47
+ async function listSites(accessToken, opts = {}) {
48
+ const top = Math.max(1, Math.min(opts.top ?? 50, 100));
49
+ if (opts.search) {
50
+ const params = new URLSearchParams({ search: opts.search, $top: String(top) });
51
+ const resp = await graphJson(
52
+ accessToken,
53
+ `/sites?${params.toString()}`
54
+ );
55
+ return resp.value;
56
+ }
57
+ const [root, followed] = await Promise.allSettled([
58
+ graphJson(accessToken, "/sites/root"),
59
+ graphJson(accessToken, "/me/followedSites")
60
+ ]);
61
+ const sites = [];
62
+ if (root.status === "fulfilled") sites.push(root.value);
63
+ if (followed.status === "fulfilled") {
64
+ for (const s of followed.value.value) {
65
+ if (!sites.find((x) => x.id === s.id)) sites.push(s);
66
+ }
67
+ }
68
+ return sites.slice(0, top);
69
+ }
70
+ async function getSite(accessToken, siteId) {
71
+ return graphJson(accessToken, `/sites/${encodeURIComponent(siteId)}`);
72
+ }
73
+ async function listDrives(accessToken, siteId) {
74
+ const resp = await graphJson(
75
+ accessToken,
76
+ `/sites/${encodeURIComponent(siteId)}/drives`
77
+ );
78
+ return resp.value;
79
+ }
80
+ async function getDefaultDrive(accessToken, siteId) {
81
+ return graphJson(
82
+ accessToken,
83
+ `/sites/${encodeURIComponent(siteId)}/drive`
84
+ );
85
+ }
86
+ async function listDriveRoot(accessToken, driveId, opts = {}) {
87
+ return listDriveChildren(accessToken, driveId, "root", opts);
88
+ }
89
+ async function listDriveChildren(accessToken, driveId, itemId, opts = {}) {
90
+ const top = Math.max(1, Math.min(opts.top ?? 50, 200));
91
+ const params = new URLSearchParams();
92
+ params.set("$top", String(top));
93
+ if (opts.skip) params.set("$skip", String(opts.skip));
94
+ params.set("$orderby", opts.orderBy || "lastModifiedDateTime desc");
95
+ let path;
96
+ if (opts.search) {
97
+ path = `/drives/${encodeURIComponent(driveId)}/root/search(q='${encodeURIComponent(
98
+ opts.search
99
+ )}')?${params.toString()}`;
100
+ } else {
101
+ const segment = itemId === "root" ? "root/children" : `items/${encodeURIComponent(itemId)}/children`;
102
+ path = `/drives/${encodeURIComponent(driveId)}/${segment}?${params.toString()}`;
103
+ }
104
+ const resp = await graphJson(accessToken, path);
105
+ return resp.value;
106
+ }
107
+ async function getDriveItem(accessToken, driveId, itemId) {
108
+ return graphJson(
109
+ accessToken,
110
+ `/drives/${encodeURIComponent(driveId)}/items/${encodeURIComponent(itemId)}`
111
+ );
112
+ }
113
+ async function downloadDriveItem(accessToken, driveId, itemId) {
114
+ const ab = await graphBinary(
115
+ accessToken,
116
+ `/drives/${encodeURIComponent(driveId)}/items/${encodeURIComponent(itemId)}/content`
117
+ );
118
+ return Buffer.from(ab);
119
+ }
120
+ async function listLists(accessToken, siteId) {
121
+ const resp = await graphJson(
122
+ accessToken,
123
+ `/sites/${encodeURIComponent(siteId)}/lists`
124
+ );
125
+ return resp.value.map((l) => ({
126
+ id: l.id,
127
+ displayName: l.displayName,
128
+ name: l.name,
129
+ webUrl: l.webUrl
130
+ }));
131
+ }
132
+ async function listListItems(accessToken, siteId, listId, top = 50) {
133
+ const params = new URLSearchParams({
134
+ $top: String(top),
135
+ expand: "fields"
136
+ });
137
+ const resp = await graphJson(
138
+ accessToken,
139
+ `/sites/${encodeURIComponent(siteId)}/lists/${encodeURIComponent(
140
+ listId
141
+ )}/items?${params.toString()}`
142
+ );
143
+ return resp.value;
144
+ }
145
+ var TEXT_MIMETYPES = /* @__PURE__ */ new Set([
146
+ "text/plain",
147
+ "text/html",
148
+ "text/markdown",
149
+ "text/csv",
150
+ "application/json",
151
+ "application/xml",
152
+ "text/xml",
153
+ "application/yaml",
154
+ "text/yaml"
155
+ ]);
156
+ var OFFICE_EXT = /\.(docx|xlsx|pptx)$/i;
157
+ var PDF_EXT = /\.pdf$/i;
158
+ async function extractTextFromDriveItem(accessToken, driveId, itemId, item) {
159
+ const meta = item || await getDriveItem(accessToken, driveId, itemId);
160
+ const mime = meta.file?.mimeType || "";
161
+ const name = meta.name || "";
162
+ const buf = await downloadDriveItem(accessToken, driveId, itemId);
163
+ if (TEXT_MIMETYPES.has(mime) || /\.(txt|md|csv|json|xml|yaml|yml|html?)$/i.test(name)) {
164
+ let txt = buf.toString("utf8");
165
+ if (/\.html?$/i.test(name) || mime === "text/html") txt = stripHtml(txt);
166
+ return { text: txt, extractedFrom: "plaintext", byteLength: buf.length };
167
+ }
168
+ if (mime === "application/pdf" || PDF_EXT.test(name)) {
169
+ try {
170
+ const pdf = await import("pdf-parse");
171
+ const result = await pdf.default(buf);
172
+ return { text: result.text || "", extractedFrom: "pdf", byteLength: buf.length };
173
+ } catch (err) {
174
+ log.warn("pdf-parse failed", { error: err instanceof Error ? err.message : String(err) });
175
+ return { text: "", extractedFrom: "pdf-failed", byteLength: buf.length };
176
+ }
177
+ }
178
+ if (OFFICE_EXT.test(name)) {
179
+ const ext = name.toLowerCase().split(".").pop();
180
+ if (ext === "docx") {
181
+ try {
182
+ const mammoth = await import("mammoth");
183
+ const result = await mammoth.extractRawText({ buffer: buf });
184
+ return { text: result.value || "", extractedFrom: "docx", byteLength: buf.length };
185
+ } catch (err) {
186
+ log.warn("mammoth failed", { error: err instanceof Error ? err.message : String(err) });
187
+ return { text: "", extractedFrom: "docx-failed", byteLength: buf.length };
188
+ }
189
+ }
190
+ if (ext === "xlsx") {
191
+ try {
192
+ const ExcelJS = await import("exceljs");
193
+ const wb = new ExcelJS.default.Workbook();
194
+ await wb.xlsx.load(buf);
195
+ const parts = [];
196
+ wb.eachSheet((sheet) => {
197
+ parts.push(`# Sheet: ${sheet.name}`);
198
+ sheet.eachRow((row) => {
199
+ const cells = [];
200
+ row.eachCell((c) => cells.push(String(c.value ?? "")));
201
+ parts.push(cells.join(" "));
202
+ });
203
+ });
204
+ return {
205
+ text: parts.join("\n"),
206
+ extractedFrom: "xlsx",
207
+ byteLength: buf.length
208
+ };
209
+ } catch (err) {
210
+ log.warn("exceljs failed", { error: err instanceof Error ? err.message : String(err) });
211
+ return { text: "", extractedFrom: "xlsx-failed", byteLength: buf.length };
212
+ }
213
+ }
214
+ return { text: "", extractedFrom: `${ext}-unsupported`, byteLength: buf.length };
215
+ }
216
+ return {
217
+ text: "",
218
+ extractedFrom: `binary:${mime || "unknown"}`,
219
+ byteLength: buf.length
220
+ };
221
+ }
222
+
223
+ export {
224
+ listSites,
225
+ getSite,
226
+ listDrives,
227
+ getDefaultDrive,
228
+ listDriveRoot,
229
+ listDriveChildren,
230
+ getDriveItem,
231
+ downloadDriveItem,
232
+ listLists,
233
+ listListItems,
234
+ extractTextFromDriveItem
235
+ };
236
+ //# sourceMappingURL=chunk-66SAOZPU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/integrations/m365/sharepoint.ts"],"sourcesContent":["/**\n * Microsoft Graph — SharePoint client.\n *\n * Read-only operations over SharePoint sites, document libraries (drives),\n * files, and list items. Reuses the bearer-token flow + error surface from\n * graph-client.ts.\n *\n * Scopes required (delegated): Sites.Read.All OR Files.Read.All for content;\n * Sites.Selected for admin-scoped deployments.\n */\n\nimport { createLogger } from \"../../core/logger\";\nimport { GraphError, stripHtml } from \"./graph-client\";\n\nconst log = createLogger(\"m365:sharepoint\");\nconst GRAPH = \"https://graph.microsoft.com/v1.0\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface SharePointSite {\n id: string;\n name?: string;\n displayName?: string;\n webUrl?: string;\n description?: string;\n createdDateTime?: string;\n lastModifiedDateTime?: string;\n}\n\nexport interface SharePointDrive {\n id: string;\n name?: string;\n driveType?: \"documentLibrary\" | \"personal\" | \"business\";\n webUrl?: string;\n description?: string;\n}\n\nexport interface DriveItem {\n id: string;\n name: string;\n webUrl?: string;\n size?: number;\n createdDateTime?: string;\n lastModifiedDateTime?: string;\n file?: { mimeType: string; hashes?: { sha256Hash?: string } };\n folder?: { childCount: number };\n parentReference?: { driveId?: string; path?: string };\n}\n\nexport interface ListItem {\n id: string;\n webUrl?: string;\n createdDateTime?: string;\n lastModifiedDateTime?: string;\n fields?: Record<string, unknown>;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nasync function graphJson<T>(accessToken: string, path: string, init?: RequestInit): Promise<T> {\n const url = path.startsWith(\"http\") ? path : `${GRAPH}${path}`;\n const res = await fetch(url, {\n ...init,\n headers: {\n Authorization: `Bearer ${accessToken}`,\n Accept: \"application/json\",\n ...(init?.headers ?? {}),\n },\n });\n if (!res.ok) {\n let body: any = null;\n try {\n body = await res.json();\n } catch {\n /* ignore */\n }\n const code = body?.error?.code || String(res.status);\n const message = body?.error?.message || res.statusText;\n log.warn(\"graph call failed\", { path, status: res.status, code });\n throw new GraphError(res.status, code, message);\n }\n if (res.status === 204) return undefined as unknown as T;\n return (await res.json()) as T;\n}\n\nasync function graphBinary(accessToken: string, path: string): Promise<ArrayBuffer> {\n const url = path.startsWith(\"http\") ? path : `${GRAPH}${path}`;\n const res = await fetch(url, {\n headers: { Authorization: `Bearer ${accessToken}` },\n redirect: \"follow\",\n });\n if (!res.ok) {\n throw new GraphError(res.status, String(res.status), res.statusText);\n }\n return res.arrayBuffer();\n}\n\n// ---------------------------------------------------------------------------\n// Sites\n// ---------------------------------------------------------------------------\n\n/**\n * List SharePoint sites the user has access to. Defaults to the user's\n * followed sites + root; supports a search query to narrow.\n */\nexport async function listSites(\n accessToken: string,\n opts: { search?: string; top?: number } = {}\n): Promise<SharePointSite[]> {\n const top = Math.max(1, Math.min(opts.top ?? 50, 100));\n\n if (opts.search) {\n const params = new URLSearchParams({ search: opts.search, $top: String(top) });\n const resp = await graphJson<{ value: SharePointSite[] }>(\n accessToken,\n `/sites?${params.toString()}`\n );\n return resp.value;\n }\n\n // No search: return root + followed (user-centric).\n const [root, followed] = await Promise.allSettled([\n graphJson<SharePointSite>(accessToken, \"/sites/root\"),\n graphJson<{ value: SharePointSite[] }>(accessToken, \"/me/followedSites\"),\n ]);\n\n const sites: SharePointSite[] = [];\n if (root.status === \"fulfilled\") sites.push(root.value);\n if (followed.status === \"fulfilled\") {\n for (const s of followed.value.value) {\n if (!sites.find((x) => x.id === s.id)) sites.push(s);\n }\n }\n return sites.slice(0, top);\n}\n\nexport async function getSite(accessToken: string, siteId: string): Promise<SharePointSite> {\n return graphJson<SharePointSite>(accessToken, `/sites/${encodeURIComponent(siteId)}`);\n}\n\n// ---------------------------------------------------------------------------\n// Drives (document libraries)\n// ---------------------------------------------------------------------------\n\nexport async function listDrives(\n accessToken: string,\n siteId: string\n): Promise<SharePointDrive[]> {\n const resp = await graphJson<{ value: SharePointDrive[] }>(\n accessToken,\n `/sites/${encodeURIComponent(siteId)}/drives`\n );\n return resp.value;\n}\n\nexport async function getDefaultDrive(\n accessToken: string,\n siteId: string\n): Promise<SharePointDrive> {\n return graphJson<SharePointDrive>(\n accessToken,\n `/sites/${encodeURIComponent(siteId)}/drive`\n );\n}\n\n// ---------------------------------------------------------------------------\n// Drive items (files and folders)\n// ---------------------------------------------------------------------------\n\nexport interface ListItemsOptions {\n top?: number;\n skip?: number;\n orderBy?: string; // e.g. \"lastModifiedDateTime desc\"\n search?: string;\n}\n\nexport async function listDriveRoot(\n accessToken: string,\n driveId: string,\n opts: ListItemsOptions = {}\n): Promise<DriveItem[]> {\n return listDriveChildren(accessToken, driveId, \"root\", opts);\n}\n\nexport async function listDriveChildren(\n accessToken: string,\n driveId: string,\n itemId: string,\n opts: ListItemsOptions = {}\n): Promise<DriveItem[]> {\n const top = Math.max(1, Math.min(opts.top ?? 50, 200));\n const params = new URLSearchParams();\n params.set(\"$top\", String(top));\n if (opts.skip) params.set(\"$skip\", String(opts.skip));\n params.set(\"$orderby\", opts.orderBy || \"lastModifiedDateTime desc\");\n\n let path: string;\n if (opts.search) {\n path = `/drives/${encodeURIComponent(driveId)}/root/search(q='${encodeURIComponent(\n opts.search\n )}')?${params.toString()}`;\n } else {\n const segment = itemId === \"root\" ? \"root/children\" : `items/${encodeURIComponent(itemId)}/children`;\n path = `/drives/${encodeURIComponent(driveId)}/${segment}?${params.toString()}`;\n }\n\n const resp = await graphJson<{ value: DriveItem[] }>(accessToken, path);\n return resp.value;\n}\n\nexport async function getDriveItem(\n accessToken: string,\n driveId: string,\n itemId: string\n): Promise<DriveItem> {\n return graphJson<DriveItem>(\n accessToken,\n `/drives/${encodeURIComponent(driveId)}/items/${encodeURIComponent(itemId)}`\n );\n}\n\n/**\n * Download a file's raw bytes. Honors Graph's redirect to the CDN URL.\n */\nexport async function downloadDriveItem(\n accessToken: string,\n driveId: string,\n itemId: string\n): Promise<Buffer> {\n const ab = await graphBinary(\n accessToken,\n `/drives/${encodeURIComponent(driveId)}/items/${encodeURIComponent(itemId)}/content`\n );\n return Buffer.from(ab);\n}\n\n// ---------------------------------------------------------------------------\n// List items (structured lists, not document libraries)\n// ---------------------------------------------------------------------------\n\nexport async function listLists(\n accessToken: string,\n siteId: string\n): Promise<Array<{ id: string; displayName: string; name?: string; webUrl?: string }>> {\n const resp = await graphJson<{ value: any[] }>(\n accessToken,\n `/sites/${encodeURIComponent(siteId)}/lists`\n );\n return resp.value.map((l) => ({\n id: l.id,\n displayName: l.displayName,\n name: l.name,\n webUrl: l.webUrl,\n }));\n}\n\nexport async function listListItems(\n accessToken: string,\n siteId: string,\n listId: string,\n top = 50\n): Promise<ListItem[]> {\n const params = new URLSearchParams({\n $top: String(top),\n expand: \"fields\",\n });\n const resp = await graphJson<{ value: ListItem[] }>(\n accessToken,\n `/sites/${encodeURIComponent(siteId)}/lists/${encodeURIComponent(\n listId\n )}/items?${params.toString()}`\n );\n return resp.value;\n}\n\n// ---------------------------------------------------------------------------\n// Content extraction — decide how to turn a file into text for LLM analysis\n// ---------------------------------------------------------------------------\n\nconst TEXT_MIMETYPES = new Set([\n \"text/plain\",\n \"text/html\",\n \"text/markdown\",\n \"text/csv\",\n \"application/json\",\n \"application/xml\",\n \"text/xml\",\n \"application/yaml\",\n \"text/yaml\",\n]);\n\nconst OFFICE_EXT = /\\.(docx|xlsx|pptx)$/i;\nconst PDF_EXT = /\\.pdf$/i;\n\nexport async function extractTextFromDriveItem(\n accessToken: string,\n driveId: string,\n itemId: string,\n item?: DriveItem\n): Promise<{ text: string; extractedFrom: string; byteLength: number }> {\n const meta = item || (await getDriveItem(accessToken, driveId, itemId));\n const mime = meta.file?.mimeType || \"\";\n const name = meta.name || \"\";\n\n const buf = await downloadDriveItem(accessToken, driveId, itemId);\n\n // Plain text / JSON / XML / CSV\n if (TEXT_MIMETYPES.has(mime) || /\\.(txt|md|csv|json|xml|yaml|yml|html?)$/i.test(name)) {\n let txt = buf.toString(\"utf8\");\n if (/\\.html?$/i.test(name) || mime === \"text/html\") txt = stripHtml(txt);\n return { text: txt, extractedFrom: \"plaintext\", byteLength: buf.length };\n }\n\n // PDF\n if (mime === \"application/pdf\" || PDF_EXT.test(name)) {\n try {\n const pdf = await import(\"pdf-parse\");\n const result = await (pdf as any).default(buf);\n return { text: result.text || \"\", extractedFrom: \"pdf\", byteLength: buf.length };\n } catch (err) {\n log.warn(\"pdf-parse failed\", { error: err instanceof Error ? err.message : String(err) });\n return { text: \"\", extractedFrom: \"pdf-failed\", byteLength: buf.length };\n }\n }\n\n // Office docs — .docx via mammoth, .xlsx via exceljs, .pptx not supported natively yet\n if (OFFICE_EXT.test(name)) {\n const ext = name.toLowerCase().split(\".\").pop();\n if (ext === \"docx\") {\n try {\n const mammoth = await import(\"mammoth\");\n const result = await (mammoth as any).extractRawText({ buffer: buf });\n return { text: result.value || \"\", extractedFrom: \"docx\", byteLength: buf.length };\n } catch (err) {\n log.warn(\"mammoth failed\", { error: err instanceof Error ? err.message : String(err) });\n return { text: \"\", extractedFrom: \"docx-failed\", byteLength: buf.length };\n }\n }\n if (ext === \"xlsx\") {\n try {\n const ExcelJS = await import(\"exceljs\");\n const wb = new (ExcelJS as any).default.Workbook();\n await wb.xlsx.load(buf);\n const parts: string[] = [];\n wb.eachSheet((sheet: any) => {\n parts.push(`# Sheet: ${sheet.name}`);\n sheet.eachRow((row: any) => {\n const cells: string[] = [];\n row.eachCell((c: any) => cells.push(String(c.value ?? \"\")));\n parts.push(cells.join(\"\\t\"));\n });\n });\n return {\n text: parts.join(\"\\n\"),\n extractedFrom: \"xlsx\",\n byteLength: buf.length,\n };\n } catch (err) {\n log.warn(\"exceljs failed\", { error: err instanceof Error ? err.message : String(err) });\n return { text: \"\", extractedFrom: \"xlsx-failed\", byteLength: buf.length };\n }\n }\n return { text: \"\", extractedFrom: `${ext}-unsupported`, byteLength: buf.length };\n }\n\n // Fallback: return a label, let caller decide\n return {\n text: \"\",\n extractedFrom: `binary:${mime || \"unknown\"}`,\n byteLength: buf.length,\n };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,113 @@
1
+ import {
2
+ decryptField,
3
+ encryptField
4
+ } from "./chunk-DTISLIMB.js";
5
+ import {
6
+ getM365Config,
7
+ refreshTokens,
8
+ shouldRefresh
9
+ } from "./chunk-3AWAWRWB.js";
10
+ import {
11
+ createLogger
12
+ } from "./chunk-7BNFELEK.js";
13
+ import {
14
+ db
15
+ } from "./chunk-5BTVJR7R.js";
16
+ import {
17
+ m365Connections
18
+ } from "./chunk-ZIBRVA3Y.js";
19
+
20
+ // src/integrations/m365/token-store.ts
21
+ import { eq } from "drizzle-orm";
22
+ var log = createLogger("m365:token-store");
23
+ async function saveConnection(input) {
24
+ const accessEnc = encryptField(input.tokens.accessToken);
25
+ const refreshEnc = input.tokens.refreshToken ? encryptField(input.tokens.refreshToken) : null;
26
+ if (!accessEnc) {
27
+ throw new Error("Failed to encrypt access token (encryption not configured?)");
28
+ }
29
+ await db.insert(m365Connections).values({
30
+ userKey: input.userKey,
31
+ email: input.email ?? null,
32
+ displayName: input.displayName ?? null,
33
+ microsoftUserId: input.microsoftUserId ?? null,
34
+ accessTokenEncrypted: accessEnc,
35
+ refreshTokenEncrypted: refreshEnc,
36
+ scope: input.tokens.scope ?? null,
37
+ tokenType: input.tokens.tokenType,
38
+ expiresAt: input.tokens.expiresAt,
39
+ updatedAt: /* @__PURE__ */ new Date()
40
+ }).onConflictDoUpdate({
41
+ target: m365Connections.userKey,
42
+ set: {
43
+ email: input.email ?? null,
44
+ displayName: input.displayName ?? null,
45
+ microsoftUserId: input.microsoftUserId ?? null,
46
+ accessTokenEncrypted: accessEnc,
47
+ refreshTokenEncrypted: refreshEnc,
48
+ scope: input.tokens.scope ?? null,
49
+ tokenType: input.tokens.tokenType,
50
+ expiresAt: input.tokens.expiresAt,
51
+ updatedAt: /* @__PURE__ */ new Date()
52
+ }
53
+ });
54
+ }
55
+ async function getConnection(userKey) {
56
+ const rows = await db.select().from(m365Connections).where(eq(m365Connections.userKey, userKey)).limit(1);
57
+ if (rows.length === 0) return null;
58
+ const row = rows[0];
59
+ return {
60
+ userKey: row.userKey,
61
+ email: row.email,
62
+ displayName: row.displayName,
63
+ microsoftUserId: row.microsoftUserId,
64
+ scope: row.scope,
65
+ expiresAt: row.expiresAt,
66
+ createdAt: row.createdAt,
67
+ updatedAt: row.updatedAt
68
+ };
69
+ }
70
+ async function deleteConnection(userKey) {
71
+ const result = await db.delete(m365Connections).where(eq(m365Connections.userKey, userKey));
72
+ return result?.count > 0 || true;
73
+ }
74
+ async function getAccessToken(userKey) {
75
+ const rows = await db.select().from(m365Connections).where(eq(m365Connections.userKey, userKey)).limit(1);
76
+ if (rows.length === 0) {
77
+ throw new Error(`No M365 connection for user ${userKey}`);
78
+ }
79
+ const row = rows[0];
80
+ if (!shouldRefresh(row.expiresAt)) {
81
+ const tok = decryptField(row.accessTokenEncrypted);
82
+ if (!tok) throw new Error("Failed to decrypt M365 access token");
83
+ return tok;
84
+ }
85
+ const refreshPlain = row.refreshTokenEncrypted ? decryptField(row.refreshTokenEncrypted) : null;
86
+ if (!refreshPlain) {
87
+ throw new Error("M365 access token expired and no refresh token available \u2014 user must reconnect");
88
+ }
89
+ const config = getM365Config();
90
+ if (!config) throw new Error("M365 is not configured");
91
+ log.info("refreshing M365 tokens", { userKey });
92
+ const fresh = await refreshTokens(config, refreshPlain);
93
+ const accessEnc = encryptField(fresh.accessToken);
94
+ const refreshEnc = fresh.refreshToken ? encryptField(fresh.refreshToken) : row.refreshTokenEncrypted;
95
+ if (!accessEnc) throw new Error("Failed to encrypt refreshed access token");
96
+ await db.update(m365Connections).set({
97
+ accessTokenEncrypted: accessEnc,
98
+ refreshTokenEncrypted: refreshEnc,
99
+ scope: fresh.scope ?? row.scope,
100
+ tokenType: fresh.tokenType,
101
+ expiresAt: fresh.expiresAt,
102
+ updatedAt: /* @__PURE__ */ new Date()
103
+ }).where(eq(m365Connections.userKey, userKey));
104
+ return fresh.accessToken;
105
+ }
106
+
107
+ export {
108
+ saveConnection,
109
+ getConnection,
110
+ deleteConnection,
111
+ getAccessToken
112
+ };
113
+ //# sourceMappingURL=chunk-6HGMRR4J.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/integrations/m365/token-store.ts"],"sourcesContent":["/**\n * Encrypted per-user token persistence for Microsoft 365.\n *\n * - Access + refresh tokens are encrypted at rest via field-encryption (AES-256-GCM).\n * - `getAccessToken(userKey)` auto-refreshes when the current token is near expiry.\n *\n * `userKey` is the stable identifier for the OpenSentinel user that owns this\n * connection — typically the email or Microsoft userPrincipalName returned by\n * /me. It is NOT the Microsoft access token; the token is stored inside the row.\n */\n\nimport { db, m365Connections } from \"../../db\";\nimport { eq } from \"drizzle-orm\";\nimport { encryptField, decryptField } from \"../../core/security/field-encryption\";\nimport {\n getM365Config,\n refreshTokens,\n shouldRefresh,\n type TokenSet,\n type M365Config,\n} from \"./oauth\";\nimport { createLogger } from \"../../core/logger\";\n\nconst log = createLogger(\"m365:token-store\");\n\nexport interface SavedConnection {\n userKey: string;\n email: string | null;\n displayName: string | null;\n microsoftUserId: string | null;\n scope: string | null;\n expiresAt: Date;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface SaveConnectionInput {\n userKey: string;\n tokens: TokenSet;\n email?: string;\n displayName?: string;\n microsoftUserId?: string;\n}\n\nexport async function saveConnection(input: SaveConnectionInput): Promise<void> {\n const accessEnc = encryptField(input.tokens.accessToken);\n const refreshEnc = input.tokens.refreshToken ? encryptField(input.tokens.refreshToken) : null;\n\n if (!accessEnc) {\n throw new Error(\"Failed to encrypt access token (encryption not configured?)\");\n }\n\n await db\n .insert(m365Connections)\n .values({\n userKey: input.userKey,\n email: input.email ?? null,\n displayName: input.displayName ?? null,\n microsoftUserId: input.microsoftUserId ?? null,\n accessTokenEncrypted: accessEnc,\n refreshTokenEncrypted: refreshEnc,\n scope: input.tokens.scope ?? null,\n tokenType: input.tokens.tokenType,\n expiresAt: input.tokens.expiresAt,\n updatedAt: new Date(),\n })\n .onConflictDoUpdate({\n target: m365Connections.userKey,\n set: {\n email: input.email ?? null,\n displayName: input.displayName ?? null,\n microsoftUserId: input.microsoftUserId ?? null,\n accessTokenEncrypted: accessEnc,\n refreshTokenEncrypted: refreshEnc,\n scope: input.tokens.scope ?? null,\n tokenType: input.tokens.tokenType,\n expiresAt: input.tokens.expiresAt,\n updatedAt: new Date(),\n },\n });\n}\n\nexport async function getConnection(userKey: string): Promise<SavedConnection | null> {\n const rows = await db\n .select()\n .from(m365Connections)\n .where(eq(m365Connections.userKey, userKey))\n .limit(1);\n if (rows.length === 0) return null;\n const row = rows[0];\n return {\n userKey: row.userKey,\n email: row.email,\n displayName: row.displayName,\n microsoftUserId: row.microsoftUserId,\n scope: row.scope,\n expiresAt: row.expiresAt,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n}\n\nexport async function deleteConnection(userKey: string): Promise<boolean> {\n const result = await db\n .delete(m365Connections)\n .where(eq(m365Connections.userKey, userKey));\n return (result as any)?.count > 0 || true; // postgres-js doesn't always return rowCount\n}\n\n/**\n * Return a valid access token for this user. Refreshes transparently if\n * the stored token is near expiry and a refresh token is available.\n * Throws if there is no connection or the refresh flow fails.\n */\nexport async function getAccessToken(userKey: string): Promise<string> {\n const rows = await db\n .select()\n .from(m365Connections)\n .where(eq(m365Connections.userKey, userKey))\n .limit(1);\n\n if (rows.length === 0) {\n throw new Error(`No M365 connection for user ${userKey}`);\n }\n const row = rows[0];\n\n if (!shouldRefresh(row.expiresAt)) {\n const tok = decryptField(row.accessTokenEncrypted);\n if (!tok) throw new Error(\"Failed to decrypt M365 access token\");\n return tok;\n }\n\n // Need to refresh.\n const refreshPlain = row.refreshTokenEncrypted ? decryptField(row.refreshTokenEncrypted) : null;\n if (!refreshPlain) {\n throw new Error(\"M365 access token expired and no refresh token available — user must reconnect\");\n }\n\n const config: M365Config | null = getM365Config();\n if (!config) throw new Error(\"M365 is not configured\");\n\n log.info(\"refreshing M365 tokens\", { userKey });\n const fresh = await refreshTokens(config, refreshPlain);\n\n const accessEnc = encryptField(fresh.accessToken);\n const refreshEnc = fresh.refreshToken ? encryptField(fresh.refreshToken) : row.refreshTokenEncrypted;\n if (!accessEnc) throw new Error(\"Failed to encrypt refreshed access token\");\n\n await db\n .update(m365Connections)\n .set({\n accessTokenEncrypted: accessEnc,\n refreshTokenEncrypted: refreshEnc,\n scope: fresh.scope ?? row.scope,\n tokenType: fresh.tokenType,\n expiresAt: fresh.expiresAt,\n updatedAt: new Date(),\n })\n .where(eq(m365Connections.userKey, userKey));\n\n return fresh.accessToken;\n}\n"],"mappings":"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","names":[]}