opensentinel 2.1.1 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (268) hide show
  1. package/README.md +354 -283
  2. package/dist/archiver-AVNBYCKQ.js +15340 -0
  3. package/dist/archiver-AVNBYCKQ.js.map +1 -0
  4. package/dist/audit-logger-OBPR7CRO.js +22 -0
  5. package/dist/auth-UOX5K2BE.js +18 -0
  6. package/dist/autonomy-ZXDBDQUJ.js +86 -0
  7. package/dist/autonomy-ZXDBDQUJ.js.map +1 -0
  8. package/dist/aws-s3-Q4LLZZPD.js +146 -0
  9. package/dist/aws-s3-Q4LLZZPD.js.map +1 -0
  10. package/dist/backup-restore-PZ7CYYB7.js +16 -0
  11. package/dist/blocks-R3PODY47.js +23 -0
  12. package/dist/bot-QRARP4UN.js +36 -0
  13. package/dist/brain-7XLLM3KC.js +56 -0
  14. package/dist/camera-monitor-M5CYKUU4.js +335 -0
  15. package/dist/camera-monitor-M5CYKUU4.js.map +1 -0
  16. package/dist/{charts-MMXM6BWW.js → charts-V7ARZNKF.js} +2 -2
  17. package/dist/chunk-22VGGA7S.js +330 -0
  18. package/dist/chunk-22VGGA7S.js.map +1 -0
  19. package/dist/chunk-35WYTA3C.js +382 -0
  20. package/dist/chunk-35WYTA3C.js.map +1 -0
  21. package/dist/chunk-3E2PSU2C.js +146 -0
  22. package/dist/chunk-3E2PSU2C.js.map +1 -0
  23. package/dist/{chunk-L3F43VPB.js → chunk-4GLYY4NN.js} +2 -2
  24. package/dist/{chunk-L3F43VPB.js.map → chunk-4GLYY4NN.js.map} +1 -1
  25. package/dist/{chunk-L3PDU3XN.js → chunk-4UOE5TUZ.js} +4 -4
  26. package/dist/{chunk-6SNHU3CY.js → chunk-66OJ3WB4.js} +2 -2
  27. package/dist/chunk-6KONMXQ6.js +297 -0
  28. package/dist/chunk-6KONMXQ6.js.map +1 -0
  29. package/dist/chunk-6PMVAAA7.js +196 -0
  30. package/dist/chunk-6PMVAAA7.js.map +1 -0
  31. package/dist/chunk-766ASQWE.js +32620 -0
  32. package/dist/chunk-766ASQWE.js.map +1 -0
  33. package/dist/chunk-7WQO5J2M.js +29 -0
  34. package/dist/chunk-7WQO5J2M.js.map +1 -0
  35. package/dist/chunk-APHSRMBS.js +148 -0
  36. package/dist/chunk-APHSRMBS.js.map +1 -0
  37. package/dist/{chunk-4LVWXUNC.js → chunk-AYUKPTSM.js} +57 -39
  38. package/dist/chunk-AYUKPTSM.js.map +1 -0
  39. package/dist/chunk-BIPYADGB.js +84 -0
  40. package/dist/chunk-BIPYADGB.js.map +1 -0
  41. package/dist/chunk-BRBWNV65.js +457 -0
  42. package/dist/chunk-BRBWNV65.js.map +1 -0
  43. package/dist/chunk-BXZ6EA52.js +382 -0
  44. package/dist/chunk-BXZ6EA52.js.map +1 -0
  45. package/dist/chunk-EVE7MIIY.js +290 -0
  46. package/dist/chunk-EVE7MIIY.js.map +1 -0
  47. package/dist/chunk-F3TTNID2.js +138 -0
  48. package/dist/chunk-F3TTNID2.js.map +1 -0
  49. package/dist/chunk-H5RQOFO2.js +190 -0
  50. package/dist/chunk-H5RQOFO2.js.map +1 -0
  51. package/dist/chunk-HN3F4WSW.js +145 -0
  52. package/dist/chunk-HN3F4WSW.js.map +1 -0
  53. package/dist/{chunk-6DRDKB45.js → chunk-I6BDYQIG.js} +20 -9
  54. package/dist/chunk-I6BDYQIG.js.map +1 -0
  55. package/dist/chunk-IZJMVV7O.js +347 -0
  56. package/dist/chunk-IZJMVV7O.js.map +1 -0
  57. package/dist/chunk-KM22GV7G.js +211 -0
  58. package/dist/chunk-KM22GV7G.js.map +1 -0
  59. package/dist/chunk-MGFBLVR7.js +103 -0
  60. package/dist/chunk-MGFBLVR7.js.map +1 -0
  61. package/dist/chunk-MQJ2ECQT.js +228 -0
  62. package/dist/chunk-MQJ2ECQT.js.map +1 -0
  63. package/dist/{chunk-F6QUZQGI.js → chunk-MXAPLSJ5.js} +2 -2
  64. package/dist/{chunk-GK3E2I7A.js → chunk-NHMBTUMW.js} +2 -2
  65. package/dist/chunk-NPRTSZIF.js +131 -0
  66. package/dist/chunk-NPRTSZIF.js.map +1 -0
  67. package/dist/chunk-O7IH7JTI.js +1898 -0
  68. package/dist/chunk-O7IH7JTI.js.map +1 -0
  69. package/dist/chunk-OCVQGBJK.js +293 -0
  70. package/dist/chunk-OCVQGBJK.js.map +1 -0
  71. package/dist/chunk-P6QINGFL.js +332 -0
  72. package/dist/chunk-P6QINGFL.js.map +1 -0
  73. package/dist/chunk-PHDZKPNE.js +91 -0
  74. package/dist/chunk-PHDZKPNE.js.map +1 -0
  75. package/dist/chunk-PLDDJCW6.js +49 -0
  76. package/dist/chunk-PTGTGXV2.js +164 -0
  77. package/dist/chunk-PTGTGXV2.js.map +1 -0
  78. package/dist/chunk-REMIY4U2.js +171 -0
  79. package/dist/chunk-REMIY4U2.js.map +1 -0
  80. package/dist/chunk-RZ4YESBG.js +141 -0
  81. package/dist/chunk-RZ4YESBG.js.map +1 -0
  82. package/dist/chunk-SAX5MHK4.js +111 -0
  83. package/dist/chunk-SAX5MHK4.js.map +1 -0
  84. package/dist/{chunk-GVJVEWHI.js → chunk-SJSUSJ47.js} +2 -2
  85. package/dist/chunk-SPPMCAKG.js +777 -0
  86. package/dist/chunk-SPPMCAKG.js.map +1 -0
  87. package/dist/chunk-SVAPX2XN.js +2441 -0
  88. package/dist/chunk-SVAPX2XN.js.map +1 -0
  89. package/dist/chunk-TVEWKIK3.js +452 -0
  90. package/dist/chunk-TVEWKIK3.js.map +1 -0
  91. package/dist/{chunk-HH2HBTQM.js → chunk-TYAGMJNV.js} +5 -5
  92. package/dist/{chunk-JXUP2X7V.js → chunk-VEHFVBLI.js} +2 -2
  93. package/dist/chunk-VNX5GMTN.js +128 -0
  94. package/dist/chunk-VNX5GMTN.js.map +1 -0
  95. package/dist/chunk-VRD5CYRL.js +1568 -0
  96. package/dist/chunk-VRD5CYRL.js.map +1 -0
  97. package/dist/chunk-WLUHNG6X.js +122 -0
  98. package/dist/chunk-WLUHNG6X.js.map +1 -0
  99. package/dist/chunk-WRAKK6K6.js +265 -0
  100. package/dist/chunk-WRAKK6K6.js.map +1 -0
  101. package/dist/chunk-XKYRH4FM.js +681 -0
  102. package/dist/chunk-XKYRH4FM.js.map +1 -0
  103. package/dist/{chunk-GUBEEYDW.js → chunk-XMCVRVTF.js} +2 -2
  104. package/dist/{chunk-GUBEEYDW.js.map → chunk-XMCVRVTF.js.map} +1 -1
  105. package/dist/chunk-ZLZKF2PM.js +310 -0
  106. package/dist/chunk-ZLZKF2PM.js.map +1 -0
  107. package/dist/cli.js +5 -1
  108. package/dist/cli.js.map +1 -1
  109. package/dist/client-ZQSFPMOB.js +21 -0
  110. package/dist/clipboard-manager-TEO2GEDN.js +24 -0
  111. package/dist/commands/setup.js +3 -3
  112. package/dist/commands/setup.js.map +1 -1
  113. package/dist/commands/start.js +3 -3
  114. package/dist/commands/status.js +2 -2
  115. package/dist/commands/stop.js +2 -2
  116. package/dist/commands/utils.js +2 -2
  117. package/dist/cron-explain-HHQKPD3M.js +16 -0
  118. package/dist/crypto-4AP47IKC.js +14 -0
  119. package/dist/crypto-4AP47IKC.js.map +1 -0
  120. package/dist/databases-37X4CI2Y.js +21 -0
  121. package/dist/databases-37X4CI2Y.js.map +1 -0
  122. package/dist/discord-B3HUPGQ6.js +70 -0
  123. package/dist/discord-B3HUPGQ6.js.map +1 -0
  124. package/dist/dist-UISMLMFN.js +21847 -0
  125. package/dist/dist-UISMLMFN.js.map +1 -0
  126. package/dist/email-K7LO2IPB.js +268 -0
  127. package/dist/email-K7LO2IPB.js.map +1 -0
  128. package/dist/enhanced-retrieval-DNLLEM4Z.js +753 -0
  129. package/dist/enhanced-retrieval-DNLLEM4Z.js.map +1 -0
  130. package/dist/enrichment-pipeline-MNHNW65K.js +13 -0
  131. package/dist/enrichment-pipeline-MNHNW65K.js.map +1 -0
  132. package/dist/entity-resolution-Y3IUWEAT.js +24 -0
  133. package/dist/entity-resolution-Y3IUWEAT.js.map +1 -0
  134. package/dist/env-IWXUVTCB.js +12 -0
  135. package/dist/env-IWXUVTCB.js.map +1 -0
  136. package/dist/google-workspace-DKWUVNGC.js +169 -0
  137. package/dist/google-workspace-DKWUVNGC.js.map +1 -0
  138. package/dist/hash-tool-ULQYD7B5.js +22 -0
  139. package/dist/hash-tool-ULQYD7B5.js.map +1 -0
  140. package/dist/heartbeat-monitor-GCISLXI3.js +22 -0
  141. package/dist/heartbeat-monitor-GCISLXI3.js.map +1 -0
  142. package/dist/image-generation-OSU7FP6F.js +486 -0
  143. package/dist/image-generation-OSU7FP6F.js.map +1 -0
  144. package/dist/imessage-NGA2XF2V.js +35 -0
  145. package/dist/imessage-NGA2XF2V.js.map +1 -0
  146. package/dist/inbox-summarizer-NRI4S7IF.js +47 -0
  147. package/dist/inbox-summarizer-NRI4S7IF.js.map +1 -0
  148. package/dist/incident-response-C5J7Q6DT.js +244 -0
  149. package/dist/incident-response-C5J7Q6DT.js.map +1 -0
  150. package/dist/inventory-manager-352OHXWD.js +24 -0
  151. package/dist/inventory-manager-352OHXWD.js.map +1 -0
  152. package/dist/jira-GSGDBMIG.js +199 -0
  153. package/dist/jira-GSGDBMIG.js.map +1 -0
  154. package/dist/json-tool-QE2SYHEG.js +26 -0
  155. package/dist/json-tool-QE2SYHEG.js.map +1 -0
  156. package/dist/key-rotation-DPHU4ZTB.js +18 -0
  157. package/dist/key-rotation-DPHU4ZTB.js.map +1 -0
  158. package/dist/lib.d.ts +603 -11
  159. package/dist/lib.js +161 -35
  160. package/dist/lib.js.map +1 -1
  161. package/dist/mailchimp-KKNF6QJ7.js +152 -0
  162. package/dist/mailchimp-KKNF6QJ7.js.map +1 -0
  163. package/dist/matrix-QVHG76I7.js +279 -0
  164. package/dist/matrix-QVHG76I7.js.map +1 -0
  165. package/dist/{mcp-LS7Q3Z5W.js → mcp-3JI6W7ZE.js} +3 -3
  166. package/dist/mcp-3JI6W7ZE.js.map +1 -0
  167. package/dist/microsoft365-UCBKJHNX.js +164 -0
  168. package/dist/microsoft365-UCBKJHNX.js.map +1 -0
  169. package/dist/ocr-AC7NPX33.js +22 -0
  170. package/dist/ocr-AC7NPX33.js.map +1 -0
  171. package/dist/ollama-BOAMSPLJ.js +8 -0
  172. package/dist/ollama-BOAMSPLJ.js.map +1 -0
  173. package/dist/pages-MI523RB7.js +26 -0
  174. package/dist/pages-MI523RB7.js.map +1 -0
  175. package/dist/pair-JDFTERIK.js +24 -0
  176. package/dist/pair-JDFTERIK.js.map +1 -0
  177. package/dist/pairing-IFQYCPNS.js +10 -0
  178. package/dist/pairing-IFQYCPNS.js.map +1 -0
  179. package/dist/pdf-ALQVOEJR.js +17 -0
  180. package/dist/pdf-ALQVOEJR.js.map +1 -0
  181. package/dist/presentations-DSV5IHG5.js +1002 -0
  182. package/dist/presentations-DSV5IHG5.js.map +1 -0
  183. package/dist/prometheus-JNT2BD4L.js +10 -0
  184. package/dist/prometheus-JNT2BD4L.js.map +1 -0
  185. package/dist/providers-J4LYPHDR.js +19 -0
  186. package/dist/providers-J4LYPHDR.js.map +1 -0
  187. package/dist/qr-code-WIX4PB4U.js +16 -0
  188. package/dist/qr-code-WIX4PB4U.js.map +1 -0
  189. package/dist/quickbooks-XB4NII2S.js +190 -0
  190. package/dist/quickbooks-XB4NII2S.js.map +1 -0
  191. package/dist/regex-tool-W4ABRKGK.js +24 -0
  192. package/dist/regex-tool-W4ABRKGK.js.map +1 -0
  193. package/dist/scheduler-VK4WFERV.js +63 -0
  194. package/dist/scheduler-VK4WFERV.js.map +1 -0
  195. package/dist/search-BCLBO5E3.js +25 -0
  196. package/dist/search-BCLBO5E3.js.map +1 -0
  197. package/dist/sendgrid-RNXCAFKM.js +152 -0
  198. package/dist/sendgrid-RNXCAFKM.js.map +1 -0
  199. package/dist/shopify-NCXYJB4R.js +171 -0
  200. package/dist/shopify-NCXYJB4R.js.map +1 -0
  201. package/dist/signal-6CGDFYL2.js +35 -0
  202. package/dist/signal-6CGDFYL2.js.map +1 -0
  203. package/dist/slack-IZQWIKOH.js +75 -0
  204. package/dist/slack-IZQWIKOH.js.map +1 -0
  205. package/dist/sms-M3JIOTCW.js +23 -0
  206. package/dist/sms-M3JIOTCW.js.map +1 -0
  207. package/dist/{src-K7GASHRH.js → src-VYUE6LRA.js} +138 -32
  208. package/dist/src-VYUE6LRA.js.map +1 -0
  209. package/dist/stocks-XXWBPOCU.js +14 -0
  210. package/dist/stocks-XXWBPOCU.js.map +1 -0
  211. package/dist/text-transform-6SGUA5Z4.js +22 -0
  212. package/dist/text-transform-6SGUA5Z4.js.map +1 -0
  213. package/dist/tools-2RLEI2N6.js +38 -0
  214. package/dist/tools-2RLEI2N6.js.map +1 -0
  215. package/dist/tunnel-IWMXUML4.js +301 -0
  216. package/dist/tunnel-IWMXUML4.js.map +1 -0
  217. package/dist/twilio-53GEW5JT.js +139 -0
  218. package/dist/twilio-53GEW5JT.js.map +1 -0
  219. package/dist/unit-converter-ZYXMEZOE.js +14 -0
  220. package/dist/unit-converter-ZYXMEZOE.js.map +1 -0
  221. package/dist/whatsapp-LFX6YKCM.js +35 -0
  222. package/dist/whatsapp-LFX6YKCM.js.map +1 -0
  223. package/dist/word-document-7B6SJMAY.js +902 -0
  224. package/dist/word-document-7B6SJMAY.js.map +1 -0
  225. package/dist/xero-QYO66D45.js +162 -0
  226. package/dist/xero-QYO66D45.js.map +1 -0
  227. package/dist/zapier-webhook-TBZ5YF2A.js +106 -0
  228. package/dist/zapier-webhook-TBZ5YF2A.js.map +1 -0
  229. package/drizzle/0002_mushy_master_mold.sql +140 -0
  230. package/drizzle/meta/0002_snapshot.json +3637 -0
  231. package/drizzle/meta/_journal.json +7 -0
  232. package/package.json +100 -98
  233. package/dist/bot-KJ26BG56.js +0 -15
  234. package/dist/chunk-4LVWXUNC.js.map +0 -1
  235. package/dist/chunk-4TG2IG5K.js +0 -5249
  236. package/dist/chunk-4TG2IG5K.js.map +0 -1
  237. package/dist/chunk-6DRDKB45.js.map +0 -1
  238. package/dist/chunk-CI6Q63MM.js +0 -1613
  239. package/dist/chunk-CI6Q63MM.js.map +0 -1
  240. package/dist/chunk-KHNYJY2Z.js +0 -178
  241. package/dist/chunk-KHNYJY2Z.js.map +0 -1
  242. package/dist/chunk-NSBPE2FW.js +0 -17
  243. package/dist/discord-ZOJFTVTB.js +0 -49
  244. package/dist/imessage-JFRB6EJ7.js +0 -14
  245. package/dist/scheduler-EZ7CZMCS.js +0 -42
  246. package/dist/signal-T3MCSULM.js +0 -14
  247. package/dist/slack-N2M4FHAJ.js +0 -54
  248. package/dist/src-K7GASHRH.js.map +0 -1
  249. package/dist/tools-24GZHYRF.js +0 -16
  250. package/dist/whatsapp-VCRUPAO5.js +0 -14
  251. /package/dist/{bot-KJ26BG56.js.map → audit-logger-OBPR7CRO.js.map} +0 -0
  252. /package/dist/{chunk-NSBPE2FW.js.map → auth-UOX5K2BE.js.map} +0 -0
  253. /package/dist/{discord-ZOJFTVTB.js.map → backup-restore-PZ7CYYB7.js.map} +0 -0
  254. /package/dist/{imessage-JFRB6EJ7.js.map → blocks-R3PODY47.js.map} +0 -0
  255. /package/dist/{mcp-LS7Q3Z5W.js.map → bot-QRARP4UN.js.map} +0 -0
  256. /package/dist/{scheduler-EZ7CZMCS.js.map → brain-7XLLM3KC.js.map} +0 -0
  257. /package/dist/{charts-MMXM6BWW.js.map → charts-V7ARZNKF.js.map} +0 -0
  258. /package/dist/{chunk-L3PDU3XN.js.map → chunk-4UOE5TUZ.js.map} +0 -0
  259. /package/dist/{chunk-6SNHU3CY.js.map → chunk-66OJ3WB4.js.map} +0 -0
  260. /package/dist/{chunk-F6QUZQGI.js.map → chunk-MXAPLSJ5.js.map} +0 -0
  261. /package/dist/{chunk-GK3E2I7A.js.map → chunk-NHMBTUMW.js.map} +0 -0
  262. /package/dist/{signal-T3MCSULM.js.map → chunk-PLDDJCW6.js.map} +0 -0
  263. /package/dist/{chunk-GVJVEWHI.js.map → chunk-SJSUSJ47.js.map} +0 -0
  264. /package/dist/{chunk-HH2HBTQM.js.map → chunk-TYAGMJNV.js.map} +0 -0
  265. /package/dist/{chunk-JXUP2X7V.js.map → chunk-VEHFVBLI.js.map} +0 -0
  266. /package/dist/{slack-N2M4FHAJ.js.map → client-ZQSFPMOB.js.map} +0 -0
  267. /package/dist/{tools-24GZHYRF.js.map → clipboard-manager-TEO2GEDN.js.map} +0 -0
  268. /package/dist/{whatsapp-VCRUPAO5.js.map → cron-explain-HHQKPD3M.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/memory.ts","../src/core/security/field-encryption.ts"],"sourcesContent":["import { db, memories, archivedMemories, type NewMemory, type Memory } from \"../db\";\r\nimport { eq, desc, sql, and } from \"drizzle-orm\";\r\nimport OpenAI from \"openai\";\r\nimport { env } from \"../config/env\";\r\nimport { encryptField, decryptField, isEncryptionAvailable } from \"./security/field-encryption\";\r\n\r\n// Lazy OpenAI client — created on first use\r\nlet _openai: OpenAI | null = null;\r\nfunction getOpenAI(): OpenAI {\r\n if (!_openai) {\r\n _openai = new OpenAI({ apiKey: env.OPENAI_API_KEY });\r\n }\r\n return _openai;\r\n}\r\nconst openai = new Proxy({} as OpenAI, {\r\n get(_target, prop) {\r\n const instance = getOpenAI();\r\n const value = (instance as any)[prop];\r\n if (typeof value === \"function\") {\r\n return value.bind(instance);\r\n }\r\n return value;\r\n },\r\n});\r\n\r\n// Generate embedding for text using OpenAI\r\nexport async function generateEmbedding(text: string): Promise<number[]> {\r\n const response = await openai.embeddings.create({\r\n model: \"text-embedding-3-small\",\r\n input: text,\r\n });\r\n return response.data[0].embedding;\r\n}\r\n\r\n// Store a new memory with embedding and tsvector\r\n// Content is encrypted at rest when ENCRYPTION_MASTER_KEY is configured\r\nexport async function storeMemory(\r\n memory: Omit<NewMemory, \"embedding\" | \"searchVector\">\r\n): Promise<Memory> {\r\n // Generate embedding from plaintext BEFORE encryption (vectors can't be encrypted)\r\n const embedding = await generateEmbedding(memory.content);\r\n\r\n // Encrypt content at rest if encryption is available\r\n const shouldEncrypt = isEncryptionAvailable();\r\n const contentForDb = shouldEncrypt ? encryptField(memory.content)! : memory.content;\r\n\r\n const [stored] = await db\r\n .insert(memories)\r\n .values({\r\n ...memory,\r\n content: contentForDb,\r\n encrypted: shouldEncrypt,\r\n embedding,\r\n provenance: memory.provenance || `${memory.source || \"unknown\"}:auto`,\r\n })\r\n .returning();\r\n\r\n // Update tsvector for full-text search (uses plaintext for indexing)\r\n try {\r\n await db.execute(sql`\r\n UPDATE memories\r\n SET search_vector = to_tsvector('english', ${memory.content})\r\n WHERE id = ${stored.id}\r\n `);\r\n } catch {\r\n // tsvector update is non-critical\r\n }\r\n\r\n // Return with plaintext content (caller expects readable content)\r\n return { ...stored, content: memory.content };\r\n}\r\n\r\n// Update an existing memory\r\nexport async function updateMemory(\r\n id: string,\r\n updates: { content?: string; type?: string; importance?: number }\r\n): Promise<Memory | null> {\r\n // Build SET clause dynamically\r\n const setClauses: any[] = [];\r\n if (updates.content) {\r\n const newEmbedding = await generateEmbedding(updates.content);\r\n setClauses.push(sql`content = ${updates.content}`);\r\n setClauses.push(sql`embedding = ${JSON.stringify(newEmbedding)}::vector`);\r\n setClauses.push(sql`search_vector = to_tsvector('english', ${updates.content})`);\r\n }\r\n if (updates.type) {\r\n setClauses.push(sql`type = ${updates.type}`);\r\n }\r\n if (updates.importance !== undefined) {\r\n setClauses.push(sql`importance = ${updates.importance}`);\r\n }\r\n\r\n if (setClauses.length === 0) return null;\r\n\r\n const result = await db.execute(sql`\r\n UPDATE memories\r\n SET ${sql.join(setClauses, sql`, `)}\r\n WHERE id = ${id}\r\n RETURNING *\r\n `);\r\n\r\n return (result as any[])[0] || null;\r\n}\r\n\r\n// Delete a memory (soft-delete: move to archived)\r\nexport async function deleteMemory(id: string): Promise<boolean> {\r\n // Get the memory first\r\n const result = await db.execute(sql`\r\n SELECT * FROM memories WHERE id = ${id}\r\n `);\r\n const memory = (result as any[])[0];\r\n if (!memory) return false;\r\n\r\n // Archive it\r\n await db.insert(archivedMemories).values({\r\n originalMemoryId: memory.id,\r\n userId: memory.user_id,\r\n type: memory.type,\r\n content: memory.content,\r\n reason: \"user_request\",\r\n originalCreatedAt: memory.created_at,\r\n });\r\n\r\n // Delete from active memories\r\n await db.execute(sql`DELETE FROM memories WHERE id = ${id}`);\r\n\r\n return true;\r\n}\r\n\r\n// Export memories as Markdown or JSON\r\nexport async function exportMemories(\r\n userId?: string,\r\n format: \"markdown\" | \"json\" = \"markdown\"\r\n): Promise<string> {\r\n const mems = await db\r\n .select()\r\n .from(memories)\r\n .where(userId ? eq(memories.userId, userId) : undefined)\r\n .orderBy(desc(memories.createdAt));\r\n\r\n // Decrypt encrypted memories\r\n for (const m of mems) {\r\n if ((m as any).encrypted) {\r\n try {\r\n (m as any).content = decryptField(m.content) ?? m.content;\r\n } catch {\r\n // If decryption fails, return as-is\r\n }\r\n }\r\n }\r\n\r\n if (format === \"json\") {\r\n return JSON.stringify(\r\n mems.map((m) => ({\r\n id: m.id,\r\n type: m.type,\r\n content: m.content,\r\n importance: m.importance,\r\n source: m.source,\r\n provenance: (m as any).provenance,\r\n createdAt: m.createdAt,\r\n })),\r\n null,\r\n 2\r\n );\r\n }\r\n\r\n // Markdown format\r\n const lines = [\r\n \"# Memories Export\",\r\n `Exported: ${new Date().toISOString()}`,\r\n `Total: ${mems.length} memories`,\r\n \"\",\r\n ];\r\n\r\n for (const m of mems) {\r\n lines.push(`## [${m.type}] (Importance: ${m.importance}/10)`);\r\n lines.push(m.content);\r\n lines.push(`_Source: ${m.source || \"unknown\"} | Created: ${m.createdAt.toISOString()}_`);\r\n lines.push(\"\");\r\n }\r\n\r\n return lines.join(\"\\n\");\r\n}\r\n\r\n// Search memories by semantic similarity\r\nexport async function searchMemories(\r\n query: string,\r\n userId?: string,\r\n limit = 5\r\n): Promise<Memory[]> {\r\n const queryEmbedding = await generateEmbedding(query);\r\n\r\n // Use pgvector cosine similarity search\r\n const results = await db.execute(sql`\r\n SELECT\r\n id, user_id, type, content, importance, source, provenance, metadata,\r\n last_accessed, created_at,\r\n 1 - (embedding <=> ${JSON.stringify(queryEmbedding)}::vector) as similarity\r\n FROM memories\r\n ${userId ? sql`WHERE user_id = ${userId}` : sql``}\r\n ORDER BY embedding <=> ${JSON.stringify(queryEmbedding)}::vector\r\n LIMIT ${limit}\r\n `);\r\n\r\n // Update last_accessed for retrieved memories\r\n const rows = results as any[];\r\n const memoryIds = rows.map((r: any) => r.id);\r\n if (memoryIds.length > 0) {\r\n await db.execute(sql`\r\n UPDATE memories\r\n SET last_accessed = NOW()\r\n WHERE id = ANY(${memoryIds}::uuid[])\r\n `);\r\n }\r\n\r\n // Decrypt content for any encrypted memories\r\n for (const row of rows) {\r\n if (row.encrypted) {\r\n try {\r\n row.content = decryptField(row.content) ?? row.content;\r\n } catch {\r\n // If decryption fails, return as-is\r\n }\r\n }\r\n }\r\n\r\n return rows as Memory[];\r\n}\r\n\r\n// Get a single memory by ID\r\nexport async function getMemoryById(id: string): Promise<Memory | null> {\r\n const result = await db.execute(sql`\r\n SELECT * FROM memories WHERE id = ${id}\r\n `);\r\n const row = (result as any[])[0] || null;\r\n if (row && row.encrypted) {\r\n try {\r\n row.content = decryptField(row.content) ?? row.content;\r\n } catch {\r\n // If decryption fails, return as-is\r\n }\r\n }\r\n return row;\r\n}\r\n\r\n// Get recent memories for a user\r\nexport async function getRecentMemories(\r\n userId: string,\r\n limit = 10\r\n): Promise<Memory[]> {\r\n return db\r\n .select()\r\n .from(memories)\r\n .where(eq(memories.userId, userId))\r\n .orderBy(desc(memories.createdAt))\r\n .limit(limit);\r\n}\r\n\r\n// Extract and store memories from a conversation turn\r\nexport async function extractMemories(\r\n content: string,\r\n userId?: string\r\n): Promise<Memory[]> {\r\n // Use Claude to extract memorable facts\r\n const extractionPrompt = `Analyze this text and extract any important facts that should be remembered about the user or their preferences. Return a JSON array of objects with \"content\" (the fact), \"type\" (semantic/episodic/procedural), and \"importance\" (1-10).\r\n\r\nText: \"${content}\"\r\n\r\nReturn only the JSON array, no other text. If no memorable facts, return [].`;\r\n\r\n try {\r\n const response = await openai.chat.completions.create({\r\n model: \"gpt-4o-mini\",\r\n messages: [{ role: \"user\", content: extractionPrompt }],\r\n response_format: { type: \"json_object\" },\r\n });\r\n\r\n const extracted = JSON.parse(\r\n response.choices[0].message.content || '{\"memories\":[]}'\r\n );\r\n const memoriesToStore = extracted.memories || extracted || [];\r\n\r\n const storedMemories: Memory[] = [];\r\n for (const mem of memoriesToStore) {\r\n if (mem.content && mem.content.length > 5) {\r\n const stored = await storeMemory({\r\n userId,\r\n content: mem.content,\r\n type: mem.type || \"semantic\",\r\n importance: mem.importance || 5,\r\n source: \"conversation\",\r\n provenance: \"extraction:auto\",\r\n });\r\n storedMemories.push(stored);\r\n }\r\n }\r\n\r\n return storedMemories;\r\n } catch (error) {\r\n console.error(\"Error extracting memories:\", error);\r\n return [];\r\n }\r\n}\r\n\r\n// Build context string from relevant memories\r\nexport async function buildMemoryContext(\r\n query: string,\r\n userId?: string,\r\n conversationHistory?: Array<{ role: \"user\" | \"assistant\"; content: string }>\r\n): Promise<string> {\r\n // Use enhanced retrieval pipeline when any advanced RAG feature is enabled\r\n const anyAdvancedEnabled = env.HYDE_ENABLED || env.RERANK_ENABLED ||\r\n env.MULTISTEP_RAG_ENABLED || env.RETRIEVAL_CACHE_ENABLED || env.CONTEXTUAL_QUERY_ENABLED;\r\n\r\n if (anyAdvancedEnabled) {\r\n try {\r\n const { enhancedRetrieve } = await import(\"./memory/enhanced-retrieval\");\r\n const result = await enhancedRetrieve(query, { userId, limit: 5, conversationHistory });\r\n\r\n if (result.results.length === 0) {\r\n return \"\";\r\n }\r\n\r\n const memoryStrings = result.results.map((m: any) => {\r\n const provenance = m.provenance ? ` [${m.provenance}]` : \"\";\r\n const score = m.rerankScore != null\r\n ? `rerank: ${m.rerankScore}/10`\r\n : `relevance: ${((m.similarity || 0) * 100).toFixed(0)}%`;\r\n return `- [${m.type}] ${m.content} (${score})${provenance}`;\r\n });\r\n\r\n return `\\n\\nRelevant memories about the user:\\n${memoryStrings.join(\"\\n\")}`;\r\n } catch {\r\n // Enhanced retrieval failed, fall back to basic search\r\n }\r\n }\r\n\r\n // Fallback: basic vector search\r\n const relevantMemories = await searchMemories(query, userId, 5);\r\n\r\n if (relevantMemories.length === 0) {\r\n return \"\";\r\n }\r\n\r\n const memoryStrings = relevantMemories.map(\r\n (m: any) => {\r\n const provenance = m.provenance ? ` [${m.provenance}]` : \"\";\r\n return `- [${m.type}] ${m.content} (relevance: ${(m.similarity * 100).toFixed(0)}%)${provenance}`;\r\n }\r\n );\r\n\r\n return `\\n\\nRelevant memories about the user:\\n${memoryStrings.join(\"\\n\")}`;\r\n}\r\n","/**\n * Field-Level Encryption for SOC 2 Compliance\n *\n * AES-256-GCM encryption for database column values.\n * Used to encrypt sensitive fields (messages, memories) at rest.\n *\n * Format: base64(keyVersion[1] + iv[16] + authTag[16] + ciphertext)\n */\n\nimport { createCipheriv, createDecipheriv, randomBytes } from \"crypto\";\nimport { env } from \"../../config/env\";\n\nconst ALGORITHM = \"aes-256-gcm\";\nconst IV_LENGTH = 16;\nconst TAG_LENGTH = 16;\nconst KEY_LENGTH = 32;\nconst CURRENT_KEY_VERSION = 1;\n\nlet _masterKey: Buffer | null = null;\n\nfunction getMasterKey(): Buffer {\n if (_masterKey) return _masterKey;\n\n const keyBase64 = env.ENCRYPTION_MASTER_KEY;\n\n if (keyBase64) {\n const key = Buffer.from(keyBase64, \"base64\");\n if (key.length !== KEY_LENGTH) {\n throw new Error(\n `ENCRYPTION_MASTER_KEY must be ${KEY_LENGTH} bytes (${KEY_LENGTH * 4 / 3} base64 chars). Got ${key.length} bytes.`\n );\n }\n _masterKey = key;\n return _masterKey;\n }\n\n // No key configured — in production this is an error\n if (env.NODE_ENV === \"production\") {\n throw new Error(\n \"ENCRYPTION_MASTER_KEY is required in production for SOC 2 compliance. \" +\n \"Generate one with: node -e \\\"console.log(require('crypto').randomBytes(32).toString('base64'))\\\"\"\n );\n }\n\n // Dev/test: generate ephemeral key (data won't survive restarts)\n console.warn(\"[field-encryption] No ENCRYPTION_MASTER_KEY set — using ephemeral key (dev only)\");\n _masterKey = randomBytes(KEY_LENGTH);\n return _masterKey;\n}\n\n/**\n * Encrypt a field value using AES-256-GCM.\n * Returns base64(keyVersion + iv + authTag + ciphertext), or null if input is null.\n */\nexport function encryptField(plaintext: string | null): string | null {\n if (plaintext === null || plaintext === undefined) return null;\n\n const key = getMasterKey();\n const iv = randomBytes(IV_LENGTH);\n const cipher = createCipheriv(ALGORITHM, key, iv);\n\n const encrypted = Buffer.concat([\n cipher.update(plaintext, \"utf8\"),\n cipher.final(),\n ]);\n const tag = cipher.getAuthTag();\n\n // keyVersion (1 byte) + iv (16) + tag (16) + ciphertext\n const combined = Buffer.concat([\n Buffer.from([CURRENT_KEY_VERSION]),\n iv,\n tag,\n encrypted,\n ]);\n\n return combined.toString(\"base64\");\n}\n\n/**\n * Decrypt a field value encrypted by encryptField().\n * Returns plaintext, or null if input is null.\n */\nexport function decryptField(encryptedBase64: string | null): string | null {\n if (encryptedBase64 === null || encryptedBase64 === undefined) return null;\n\n const combined = Buffer.from(encryptedBase64, \"base64\");\n\n if (combined.length < 1 + IV_LENGTH + TAG_LENGTH + 1) {\n throw new Error(\"Encrypted data too short\");\n }\n\n const keyVersion = combined[0];\n if (keyVersion !== CURRENT_KEY_VERSION) {\n throw new Error(`Unsupported encryption key version: ${keyVersion}`);\n }\n\n const iv = combined.subarray(1, 1 + IV_LENGTH);\n const tag = combined.subarray(1 + IV_LENGTH, 1 + IV_LENGTH + TAG_LENGTH);\n const ciphertext = combined.subarray(1 + IV_LENGTH + TAG_LENGTH);\n\n const key = getMasterKey();\n const decipher = createDecipheriv(ALGORITHM, key, iv);\n decipher.setAuthTag(tag);\n\n const decrypted = Buffer.concat([\n decipher.update(ciphertext),\n decipher.final(),\n ]);\n\n return decrypted.toString(\"utf8\");\n}\n\n/**\n * Encrypt multiple fields at once.\n */\nexport function encryptFields(\n fields: Record<string, string | null>\n): Record<string, string | null> {\n const result: Record<string, string | null> = {};\n for (const [k, v] of Object.entries(fields)) {\n result[k] = encryptField(v);\n }\n return result;\n}\n\n/**\n * Decrypt multiple fields at once.\n */\nexport function decryptFields(\n fields: Record<string, string | null>\n): Record<string, string | null> {\n const result: Record<string, string | null> = {};\n for (const [k, v] of Object.entries(fields)) {\n result[k] = decryptField(v);\n }\n return result;\n}\n\n/**\n * Generate a new random 32-byte encryption key, returned as base64.\n * Use this to create an ENCRYPTION_MASTER_KEY for .env.\n */\nexport function generateEncryptionKey(): string {\n return randomBytes(KEY_LENGTH).toString(\"base64\");\n}\n\n/**\n * Check whether field encryption is available (master key is configured).\n */\nexport function isEncryptionAvailable(): boolean {\n try {\n getMasterKey();\n return true;\n } catch {\n return false;\n }\n}\n"],"mappings":";;;;;;;;;;AACA,SAAS,IAAI,MAAM,WAAgB;AACnC,OAAO,YAAY;;;ACOnB,SAAS,gBAAgB,kBAAkB,mBAAmB;AAG9D,IAAM,YAAY;AAClB,IAAM,YAAY;AAClB,IAAM,aAAa;AACnB,IAAM,aAAa;AACnB,IAAM,sBAAsB;AAE5B,IAAI,aAA4B;AAEhC,SAAS,eAAuB;AAC9B,MAAI,WAAY,QAAO;AAEvB,QAAM,YAAY,IAAI;AAEtB,MAAI,WAAW;AACb,UAAM,MAAM,OAAO,KAAK,WAAW,QAAQ;AAC3C,QAAI,IAAI,WAAW,YAAY;AAC7B,YAAM,IAAI;AAAA,QACR,iCAAiC,UAAU,WAAW,aAAa,IAAI,CAAC,uBAAuB,IAAI,MAAM;AAAA,MAC3G;AAAA,IACF;AACA,iBAAa;AACb,WAAO;AAAA,EACT;AAGA,MAAI,IAAI,aAAa,cAAc;AACjC,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AAGA,UAAQ,KAAK,uFAAkF;AAC/F,eAAa,YAAY,UAAU;AACnC,SAAO;AACT;AAMO,SAAS,aAAa,WAAyC;AACpE,MAAI,cAAc,QAAQ,cAAc,OAAW,QAAO;AAE1D,QAAM,MAAM,aAAa;AACzB,QAAM,KAAK,YAAY,SAAS;AAChC,QAAM,SAAS,eAAe,WAAW,KAAK,EAAE;AAEhD,QAAM,YAAY,OAAO,OAAO;AAAA,IAC9B,OAAO,OAAO,WAAW,MAAM;AAAA,IAC/B,OAAO,MAAM;AAAA,EACf,CAAC;AACD,QAAM,MAAM,OAAO,WAAW;AAG9B,QAAM,WAAW,OAAO,OAAO;AAAA,IAC7B,OAAO,KAAK,CAAC,mBAAmB,CAAC;AAAA,IACjC;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,SAAO,SAAS,SAAS,QAAQ;AACnC;AAMO,SAAS,aAAa,iBAA+C;AAC1E,MAAI,oBAAoB,QAAQ,oBAAoB,OAAW,QAAO;AAEtE,QAAM,WAAW,OAAO,KAAK,iBAAiB,QAAQ;AAEtD,MAAI,SAAS,SAAS,IAAI,YAAY,aAAa,GAAG;AACpD,UAAM,IAAI,MAAM,0BAA0B;AAAA,EAC5C;AAEA,QAAM,aAAa,SAAS,CAAC;AAC7B,MAAI,eAAe,qBAAqB;AACtC,UAAM,IAAI,MAAM,uCAAuC,UAAU,EAAE;AAAA,EACrE;AAEA,QAAM,KAAK,SAAS,SAAS,GAAG,IAAI,SAAS;AAC7C,QAAM,MAAM,SAAS,SAAS,IAAI,WAAW,IAAI,YAAY,UAAU;AACvE,QAAM,aAAa,SAAS,SAAS,IAAI,YAAY,UAAU;AAE/D,QAAM,MAAM,aAAa;AACzB,QAAM,WAAW,iBAAiB,WAAW,KAAK,EAAE;AACpD,WAAS,WAAW,GAAG;AAEvB,QAAM,YAAY,OAAO,OAAO;AAAA,IAC9B,SAAS,OAAO,UAAU;AAAA,IAC1B,SAAS,MAAM;AAAA,EACjB,CAAC;AAED,SAAO,UAAU,SAAS,MAAM;AAClC;AAuCO,SAAS,wBAAiC;AAC/C,MAAI;AACF,iBAAa;AACb,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ADrJA,IAAI,UAAyB;AAC7B,SAAS,YAAoB;AAC3B,MAAI,CAAC,SAAS;AACZ,cAAU,IAAI,OAAO,EAAE,QAAQ,IAAI,eAAe,CAAC;AAAA,EACrD;AACA,SAAO;AACT;AACA,IAAM,SAAS,IAAI,MAAM,CAAC,GAAa;AAAA,EACrC,IAAI,SAAS,MAAM;AACjB,UAAM,WAAW,UAAU;AAC3B,UAAM,QAAS,SAAiB,IAAI;AACpC,QAAI,OAAO,UAAU,YAAY;AAC/B,aAAO,MAAM,KAAK,QAAQ;AAAA,IAC5B;AACA,WAAO;AAAA,EACT;AACF,CAAC;AAGD,eAAsB,kBAAkB,MAAiC;AACvE,QAAM,WAAW,MAAM,OAAO,WAAW,OAAO;AAAA,IAC9C,OAAO;AAAA,IACP,OAAO;AAAA,EACT,CAAC;AACD,SAAO,SAAS,KAAK,CAAC,EAAE;AAC1B;AAIA,eAAsB,YACpB,QACiB;AAEjB,QAAM,YAAY,MAAM,kBAAkB,OAAO,OAAO;AAGxD,QAAM,gBAAgB,sBAAsB;AAC5C,QAAM,eAAe,gBAAgB,aAAa,OAAO,OAAO,IAAK,OAAO;AAE5E,QAAM,CAAC,MAAM,IAAI,MAAM,GACpB,OAAO,QAAQ,EACf,OAAO;AAAA,IACN,GAAG;AAAA,IACH,SAAS;AAAA,IACT,WAAW;AAAA,IACX;AAAA,IACA,YAAY,OAAO,cAAc,GAAG,OAAO,UAAU,SAAS;AAAA,EAChE,CAAC,EACA,UAAU;AAGb,MAAI;AACF,UAAM,GAAG,QAAQ;AAAA;AAAA,mDAE8B,OAAO,OAAO;AAAA,mBAC9C,OAAO,EAAE;AAAA,KACvB;AAAA,EACH,QAAQ;AAAA,EAER;AAGA,SAAO,EAAE,GAAG,QAAQ,SAAS,OAAO,QAAQ;AAC9C;AAGA,eAAsB,aACpB,IACA,SACwB;AAExB,QAAM,aAAoB,CAAC;AAC3B,MAAI,QAAQ,SAAS;AACnB,UAAM,eAAe,MAAM,kBAAkB,QAAQ,OAAO;AAC5D,eAAW,KAAK,gBAAgB,QAAQ,OAAO,EAAE;AACjD,eAAW,KAAK,kBAAkB,KAAK,UAAU,YAAY,CAAC,UAAU;AACxE,eAAW,KAAK,6CAA6C,QAAQ,OAAO,GAAG;AAAA,EACjF;AACA,MAAI,QAAQ,MAAM;AAChB,eAAW,KAAK,aAAa,QAAQ,IAAI,EAAE;AAAA,EAC7C;AACA,MAAI,QAAQ,eAAe,QAAW;AACpC,eAAW,KAAK,mBAAmB,QAAQ,UAAU,EAAE;AAAA,EACzD;AAEA,MAAI,WAAW,WAAW,EAAG,QAAO;AAEpC,QAAM,SAAS,MAAM,GAAG,QAAQ;AAAA;AAAA,UAExB,IAAI,KAAK,YAAY,OAAO,CAAC;AAAA,iBACtB,EAAE;AAAA;AAAA,GAEhB;AAED,SAAQ,OAAiB,CAAC,KAAK;AACjC;AAGA,eAAsB,aAAa,IAA8B;AAE/D,QAAM,SAAS,MAAM,GAAG,QAAQ;AAAA,wCACM,EAAE;AAAA,GACvC;AACD,QAAM,SAAU,OAAiB,CAAC;AAClC,MAAI,CAAC,OAAQ,QAAO;AAGpB,QAAM,GAAG,OAAO,gBAAgB,EAAE,OAAO;AAAA,IACvC,kBAAkB,OAAO;AAAA,IACzB,QAAQ,OAAO;AAAA,IACf,MAAM,OAAO;AAAA,IACb,SAAS,OAAO;AAAA,IAChB,QAAQ;AAAA,IACR,mBAAmB,OAAO;AAAA,EAC5B,CAAC;AAGD,QAAM,GAAG,QAAQ,sCAAsC,EAAE,EAAE;AAE3D,SAAO;AACT;AAGA,eAAsB,eACpB,QACA,SAA8B,YACb;AACjB,QAAM,OAAO,MAAM,GAChB,OAAO,EACP,KAAK,QAAQ,EACb,MAAM,SAAS,GAAG,SAAS,QAAQ,MAAM,IAAI,MAAS,EACtD,QAAQ,KAAK,SAAS,SAAS,CAAC;AAGnC,aAAW,KAAK,MAAM;AACpB,QAAK,EAAU,WAAW;AACxB,UAAI;AACF,QAAC,EAAU,UAAU,aAAa,EAAE,OAAO,KAAK,EAAE;AAAA,MACpD,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAEA,MAAI,WAAW,QAAQ;AACrB,WAAO,KAAK;AAAA,MACV,KAAK,IAAI,CAAC,OAAO;AAAA,QACf,IAAI,EAAE;AAAA,QACN,MAAM,EAAE;AAAA,QACR,SAAS,EAAE;AAAA,QACX,YAAY,EAAE;AAAA,QACd,QAAQ,EAAE;AAAA,QACV,YAAa,EAAU;AAAA,QACvB,WAAW,EAAE;AAAA,MACf,EAAE;AAAA,MACF;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAGA,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA,cAAa,oBAAI,KAAK,GAAE,YAAY,CAAC;AAAA,IACrC,UAAU,KAAK,MAAM;AAAA,IACrB;AAAA,EACF;AAEA,aAAW,KAAK,MAAM;AACpB,UAAM,KAAK,OAAO,EAAE,IAAI,kBAAkB,EAAE,UAAU,MAAM;AAC5D,UAAM,KAAK,EAAE,OAAO;AACpB,UAAM,KAAK,YAAY,EAAE,UAAU,SAAS,eAAe,EAAE,UAAU,YAAY,CAAC,GAAG;AACvF,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAGA,eAAsB,eACpB,OACA,QACA,QAAQ,GACW;AACnB,QAAM,iBAAiB,MAAM,kBAAkB,KAAK;AAGpD,QAAM,UAAU,MAAM,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,2BAIR,KAAK,UAAU,cAAc,CAAC;AAAA;AAAA,MAEnD,SAAS,sBAAsB,MAAM,KAAK,KAAK;AAAA,6BACxB,KAAK,UAAU,cAAc,CAAC;AAAA,YAC/C,KAAK;AAAA,GACd;AAGD,QAAM,OAAO;AACb,QAAM,YAAY,KAAK,IAAI,CAAC,MAAW,EAAE,EAAE;AAC3C,MAAI,UAAU,SAAS,GAAG;AACxB,UAAM,GAAG,QAAQ;AAAA;AAAA;AAAA,uBAGE,SAAS;AAAA,KAC3B;AAAA,EACH;AAGA,aAAW,OAAO,MAAM;AACtB,QAAI,IAAI,WAAW;AACjB,UAAI;AACF,YAAI,UAAU,aAAa,IAAI,OAAO,KAAK,IAAI;AAAA,MACjD,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAGA,eAAsB,cAAc,IAAoC;AACtE,QAAM,SAAS,MAAM,GAAG,QAAQ;AAAA,wCACM,EAAE;AAAA,GACvC;AACD,QAAM,MAAO,OAAiB,CAAC,KAAK;AACpC,MAAI,OAAO,IAAI,WAAW;AACxB,QAAI;AACF,UAAI,UAAU,aAAa,IAAI,OAAO,KAAK,IAAI;AAAA,IACjD,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AAgBA,eAAsB,gBACpB,SACA,QACmB;AAEnB,QAAM,mBAAmB;AAAA;AAAA,SAElB,OAAO;AAAA;AAAA;AAId,MAAI;AACF,UAAM,WAAW,MAAM,OAAO,KAAK,YAAY,OAAO;AAAA,MACpD,OAAO;AAAA,MACP,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,iBAAiB,CAAC;AAAA,MACtD,iBAAiB,EAAE,MAAM,cAAc;AAAA,IACzC,CAAC;AAED,UAAM,YAAY,KAAK;AAAA,MACrB,SAAS,QAAQ,CAAC,EAAE,QAAQ,WAAW;AAAA,IACzC;AACA,UAAM,kBAAkB,UAAU,YAAY,aAAa,CAAC;AAE5D,UAAM,iBAA2B,CAAC;AAClC,eAAW,OAAO,iBAAiB;AACjC,UAAI,IAAI,WAAW,IAAI,QAAQ,SAAS,GAAG;AACzC,cAAM,SAAS,MAAM,YAAY;AAAA,UAC/B;AAAA,UACA,SAAS,IAAI;AAAA,UACb,MAAM,IAAI,QAAQ;AAAA,UAClB,YAAY,IAAI,cAAc;AAAA,UAC9B,QAAQ;AAAA,UACR,YAAY;AAAA,QACd,CAAC;AACD,uBAAe,KAAK,MAAM;AAAA,MAC5B;AAAA,IACF;AAEA,WAAO;AAAA,EACT,SAAS,OAAO;AACd,YAAQ,MAAM,8BAA8B,KAAK;AACjD,WAAO,CAAC;AAAA,EACV;AACF;AAGA,eAAsB,mBACpB,OACA,QACA,qBACiB;AAEjB,QAAM,qBAAqB,IAAI,gBAAgB,IAAI,kBACjD,IAAI,yBAAyB,IAAI,2BAA2B,IAAI;AAElE,MAAI,oBAAoB;AACtB,QAAI;AACF,YAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,kCAA6B;AACvE,YAAM,SAAS,MAAM,iBAAiB,OAAO,EAAE,QAAQ,OAAO,GAAG,oBAAoB,CAAC;AAEtF,UAAI,OAAO,QAAQ,WAAW,GAAG;AAC/B,eAAO;AAAA,MACT;AAEA,YAAMA,iBAAgB,OAAO,QAAQ,IAAI,CAAC,MAAW;AACnD,cAAM,aAAa,EAAE,aAAa,KAAK,EAAE,UAAU,MAAM;AACzD,cAAM,QAAQ,EAAE,eAAe,OAC3B,WAAW,EAAE,WAAW,QACxB,gBAAgB,EAAE,cAAc,KAAK,KAAK,QAAQ,CAAC,CAAC;AACxD,eAAO,MAAM,EAAE,IAAI,KAAK,EAAE,OAAO,KAAK,KAAK,IAAI,UAAU;AAAA,MAC3D,CAAC;AAED,aAAO;AAAA;AAAA;AAAA,EAA0CA,eAAc,KAAK,IAAI,CAAC;AAAA,IAC3E,QAAQ;AAAA,IAER;AAAA,EACF;AAGA,QAAM,mBAAmB,MAAM,eAAe,OAAO,QAAQ,CAAC;AAE9D,MAAI,iBAAiB,WAAW,GAAG;AACjC,WAAO;AAAA,EACT;AAEA,QAAM,gBAAgB,iBAAiB;AAAA,IACrC,CAAC,MAAW;AACV,YAAM,aAAa,EAAE,aAAa,KAAK,EAAE,UAAU,MAAM;AACzD,aAAO,MAAM,EAAE,IAAI,KAAK,EAAE,OAAO,iBAAiB,EAAE,aAAa,KAAK,QAAQ,CAAC,CAAC,KAAK,UAAU;AAAA,IACjG;AAAA,EACF;AAEA,SAAO;AAAA;AAAA;AAAA,EAA0C,cAAc,KAAK,IAAI,CAAC;AAC3E;","names":["memoryStrings"]}
@@ -0,0 +1,211 @@
1
+ // src/core/observability/prometheus.ts
2
+ var COUNTER_DEFINITIONS = [
3
+ {
4
+ name: "opensentinel_requests_total",
5
+ help: "Total number of requests processed",
6
+ labelNames: ["model", "channel"]
7
+ },
8
+ {
9
+ name: "opensentinel_tokens_input_total",
10
+ help: "Total number of input tokens consumed",
11
+ labelNames: ["model"]
12
+ },
13
+ {
14
+ name: "opensentinel_tokens_output_total",
15
+ help: "Total number of output tokens produced",
16
+ labelNames: ["model"]
17
+ },
18
+ {
19
+ name: "opensentinel_errors_total",
20
+ help: "Total number of errors encountered",
21
+ labelNames: ["type"]
22
+ },
23
+ {
24
+ name: "opensentinel_tool_executions_total",
25
+ help: "Total number of tool executions",
26
+ labelNames: ["tool", "success"]
27
+ }
28
+ ];
29
+ var HISTOGRAM_DEFINITIONS = [
30
+ {
31
+ name: "opensentinel_response_latency_ms",
32
+ help: "Response latency in milliseconds",
33
+ buckets: [10, 25, 50, 100, 250, 500, 1e3, 2500, 5e3, 1e4, 3e4]
34
+ },
35
+ {
36
+ name: "opensentinel_tool_duration_ms",
37
+ help: "Tool execution duration in milliseconds",
38
+ buckets: [10, 25, 50, 100, 250, 500, 1e3, 2500, 5e3, 1e4, 3e4]
39
+ }
40
+ ];
41
+ var GAUGE_DEFINITIONS = [
42
+ {
43
+ name: "opensentinel_uptime_seconds",
44
+ help: "Process uptime in seconds"
45
+ },
46
+ {
47
+ name: "opensentinel_memory_heap_bytes",
48
+ help: "Process heap memory usage in bytes"
49
+ }
50
+ ];
51
+ function labelKey(labels) {
52
+ return Object.keys(labels).sort().map((k) => `${k}="${labels[k]}"`).join(",");
53
+ }
54
+ function formatLabels(labels) {
55
+ const parts = Object.keys(labels).sort().map((k) => `${k}="${labels[k]}"`);
56
+ return parts.length > 0 ? `{${parts.join(",")}}` : "";
57
+ }
58
+ var PrometheusExporter = class {
59
+ // Counters: Map<metricName, Map<labelKey, { labels, value }>>
60
+ counters = /* @__PURE__ */ new Map();
61
+ // Histograms: Map<metricName, observedValues[]>
62
+ histograms = /* @__PURE__ */ new Map();
63
+ startTime;
64
+ constructor() {
65
+ this.startTime = Date.now();
66
+ for (const def of COUNTER_DEFINITIONS) {
67
+ this.counters.set(def.name, /* @__PURE__ */ new Map());
68
+ }
69
+ for (const def of HISTOGRAM_DEFINITIONS) {
70
+ this.histograms.set(def.name, []);
71
+ }
72
+ }
73
+ // -----------------------------------------------------------------------
74
+ // Generic counter / histogram operations
75
+ // -----------------------------------------------------------------------
76
+ /**
77
+ * Increment a counter metric by `value` (default 1).
78
+ */
79
+ incrementCounter(name, labels, value = 1) {
80
+ const counterMap = this.counters.get(name);
81
+ if (!counterMap) {
82
+ return;
83
+ }
84
+ const key = labelKey(labels);
85
+ const existing = counterMap.get(key);
86
+ if (existing) {
87
+ existing.value += value;
88
+ } else {
89
+ counterMap.set(key, { labels: { ...labels }, value });
90
+ }
91
+ }
92
+ /**
93
+ * Record an observed value for a histogram metric.
94
+ */
95
+ observeHistogram(name, value) {
96
+ const observations = this.histograms.get(name);
97
+ if (!observations) {
98
+ return;
99
+ }
100
+ observations.push(value);
101
+ }
102
+ // -----------------------------------------------------------------------
103
+ // Convenience recording methods
104
+ // -----------------------------------------------------------------------
105
+ /**
106
+ * Record a request against the requests counter.
107
+ */
108
+ recordRequest(model, channel) {
109
+ this.incrementCounter("opensentinel_requests_total", { model, channel });
110
+ }
111
+ /**
112
+ * Record input and output token counts.
113
+ */
114
+ recordTokens(model, inputTokens, outputTokens) {
115
+ this.incrementCounter("opensentinel_tokens_input_total", { model }, inputTokens);
116
+ this.incrementCounter("opensentinel_tokens_output_total", { model }, outputTokens);
117
+ }
118
+ /**
119
+ * Record an error occurrence.
120
+ */
121
+ recordError(type) {
122
+ this.incrementCounter("opensentinel_errors_total", { type });
123
+ }
124
+ /**
125
+ * Record a tool execution (success/failure) and its duration.
126
+ */
127
+ recordToolExecution(tool, success, durationMs) {
128
+ this.incrementCounter("opensentinel_tool_executions_total", {
129
+ tool,
130
+ success: String(success)
131
+ });
132
+ this.observeHistogram("opensentinel_tool_duration_ms", durationMs);
133
+ }
134
+ /**
135
+ * Record overall response latency.
136
+ */
137
+ recordLatency(durationMs) {
138
+ this.observeHistogram("opensentinel_response_latency_ms", durationMs);
139
+ }
140
+ // -----------------------------------------------------------------------
141
+ // Prometheus text exposition output
142
+ // -----------------------------------------------------------------------
143
+ /**
144
+ * Produce the full metrics payload in Prometheus text exposition format
145
+ * (`text/plain; version=0.0.4`).
146
+ */
147
+ toTextFormat() {
148
+ const lines = [];
149
+ for (const def of COUNTER_DEFINITIONS) {
150
+ lines.push(`# HELP ${def.name} ${def.help}`);
151
+ lines.push(`# TYPE ${def.name} counter`);
152
+ const counterMap = this.counters.get(def.name);
153
+ if (counterMap) {
154
+ for (const entry of counterMap.values()) {
155
+ lines.push(`${def.name}${formatLabels(entry.labels)} ${entry.value}`);
156
+ }
157
+ }
158
+ lines.push("");
159
+ }
160
+ for (const def of HISTOGRAM_DEFINITIONS) {
161
+ lines.push(`# HELP ${def.name} ${def.help}`);
162
+ lines.push(`# TYPE ${def.name} histogram`);
163
+ const observations = this.histograms.get(def.name) ?? [];
164
+ const count = observations.length;
165
+ const sum = observations.reduce((a, b) => a + b, 0);
166
+ for (const le of def.buckets) {
167
+ const bucketCount = observations.filter((v) => v <= le).length;
168
+ lines.push(`${def.name}_bucket{le="${le}"} ${bucketCount}`);
169
+ }
170
+ lines.push(`${def.name}_bucket{le="+Inf"} ${count}`);
171
+ lines.push(`${def.name}_sum ${sum}`);
172
+ lines.push(`${def.name}_count ${count}`);
173
+ lines.push("");
174
+ }
175
+ for (const def of GAUGE_DEFINITIONS) {
176
+ lines.push(`# HELP ${def.name} ${def.help}`);
177
+ lines.push(`# TYPE ${def.name} gauge`);
178
+ if (def.name === "opensentinel_uptime_seconds") {
179
+ const uptimeSeconds = (Date.now() - this.startTime) / 1e3;
180
+ lines.push(`${def.name} ${uptimeSeconds}`);
181
+ } else if (def.name === "opensentinel_memory_heap_bytes") {
182
+ const heapUsed = process.memoryUsage().heapUsed;
183
+ lines.push(`${def.name} ${heapUsed}`);
184
+ }
185
+ lines.push("");
186
+ }
187
+ return lines.join("\n");
188
+ }
189
+ // -----------------------------------------------------------------------
190
+ // Reset
191
+ // -----------------------------------------------------------------------
192
+ /**
193
+ * Reset all metrics to their initial state.
194
+ */
195
+ reset() {
196
+ for (const def of COUNTER_DEFINITIONS) {
197
+ this.counters.set(def.name, /* @__PURE__ */ new Map());
198
+ }
199
+ for (const def of HISTOGRAM_DEFINITIONS) {
200
+ this.histograms.set(def.name, []);
201
+ }
202
+ this.startTime = Date.now();
203
+ }
204
+ };
205
+ var prometheusExporter = new PrometheusExporter();
206
+
207
+ export {
208
+ PrometheusExporter,
209
+ prometheusExporter
210
+ };
211
+ //# sourceMappingURL=chunk-KM22GV7G.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/observability/prometheus.ts"],"sourcesContent":["/**\n * Prometheus metrics exporter for OpenSentinel.\n *\n * Outputs metrics in the Prometheus text exposition format\n * (text/plain; version=0.0.4) for scraping by a Prometheus server.\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface CounterDefinition {\n name: string;\n help: string;\n labelNames: string[];\n}\n\ninterface HistogramDefinition {\n name: string;\n help: string;\n buckets: number[];\n}\n\ninterface GaugeDefinition {\n name: string;\n help: string;\n}\n\n// ---------------------------------------------------------------------------\n// Definitions\n// ---------------------------------------------------------------------------\n\nconst COUNTER_DEFINITIONS: CounterDefinition[] = [\n {\n name: \"opensentinel_requests_total\",\n help: \"Total number of requests processed\",\n labelNames: [\"model\", \"channel\"],\n },\n {\n name: \"opensentinel_tokens_input_total\",\n help: \"Total number of input tokens consumed\",\n labelNames: [\"model\"],\n },\n {\n name: \"opensentinel_tokens_output_total\",\n help: \"Total number of output tokens produced\",\n labelNames: [\"model\"],\n },\n {\n name: \"opensentinel_errors_total\",\n help: \"Total number of errors encountered\",\n labelNames: [\"type\"],\n },\n {\n name: \"opensentinel_tool_executions_total\",\n help: \"Total number of tool executions\",\n labelNames: [\"tool\", \"success\"],\n },\n];\n\nconst HISTOGRAM_DEFINITIONS: HistogramDefinition[] = [\n {\n name: \"opensentinel_response_latency_ms\",\n help: \"Response latency in milliseconds\",\n buckets: [10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000, 30000],\n },\n {\n name: \"opensentinel_tool_duration_ms\",\n help: \"Tool execution duration in milliseconds\",\n buckets: [10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000, 30000],\n },\n];\n\nconst GAUGE_DEFINITIONS: GaugeDefinition[] = [\n {\n name: \"opensentinel_uptime_seconds\",\n help: \"Process uptime in seconds\",\n },\n {\n name: \"opensentinel_memory_heap_bytes\",\n help: \"Process heap memory usage in bytes\",\n },\n];\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Build a label key string from a labels record so it can be used as a Map\n * key. Labels are sorted alphabetically to ensure deterministic ordering.\n *\n * Example: `{model: \"claude\", channel: \"telegram\"}` -> `channel=\"telegram\",model=\"claude\"`\n */\nfunction labelKey(labels: Record<string, string>): string {\n return Object.keys(labels)\n .sort()\n .map((k) => `${k}=\"${labels[k]}\"`)\n .join(\",\");\n}\n\n/**\n * Format a labels record into the Prometheus `{key=\"value\",...}` syntax.\n */\nfunction formatLabels(labels: Record<string, string>): string {\n const parts = Object.keys(labels)\n .sort()\n .map((k) => `${k}=\"${labels[k]}\"`);\n return parts.length > 0 ? `{${parts.join(\",\")}}` : \"\";\n}\n\n// ---------------------------------------------------------------------------\n// PrometheusExporter\n// ---------------------------------------------------------------------------\n\nexport class PrometheusExporter {\n // Counters: Map<metricName, Map<labelKey, { labels, value }>>\n private counters: Map<\n string,\n Map<string, { labels: Record<string, string>; value: number }>\n > = new Map();\n\n // Histograms: Map<metricName, observedValues[]>\n private histograms: Map<string, number[]> = new Map();\n\n private startTime: number;\n\n constructor() {\n this.startTime = Date.now();\n\n // Initialise counter maps\n for (const def of COUNTER_DEFINITIONS) {\n this.counters.set(def.name, new Map());\n }\n\n // Initialise histogram arrays\n for (const def of HISTOGRAM_DEFINITIONS) {\n this.histograms.set(def.name, []);\n }\n }\n\n // -----------------------------------------------------------------------\n // Generic counter / histogram operations\n // -----------------------------------------------------------------------\n\n /**\n * Increment a counter metric by `value` (default 1).\n */\n incrementCounter(\n name: string,\n labels: Record<string, string>,\n value: number = 1,\n ): void {\n const counterMap = this.counters.get(name);\n if (!counterMap) {\n return;\n }\n\n const key = labelKey(labels);\n const existing = counterMap.get(key);\n if (existing) {\n existing.value += value;\n } else {\n counterMap.set(key, { labels: { ...labels }, value });\n }\n }\n\n /**\n * Record an observed value for a histogram metric.\n */\n observeHistogram(name: string, value: number): void {\n const observations = this.histograms.get(name);\n if (!observations) {\n return;\n }\n observations.push(value);\n }\n\n // -----------------------------------------------------------------------\n // Convenience recording methods\n // -----------------------------------------------------------------------\n\n /**\n * Record a request against the requests counter.\n */\n recordRequest(model: string, channel: string): void {\n this.incrementCounter(\"opensentinel_requests_total\", { model, channel });\n }\n\n /**\n * Record input and output token counts.\n */\n recordTokens(model: string, inputTokens: number, outputTokens: number): void {\n this.incrementCounter(\"opensentinel_tokens_input_total\", { model }, inputTokens);\n this.incrementCounter(\"opensentinel_tokens_output_total\", { model }, outputTokens);\n }\n\n /**\n * Record an error occurrence.\n */\n recordError(type: string): void {\n this.incrementCounter(\"opensentinel_errors_total\", { type });\n }\n\n /**\n * Record a tool execution (success/failure) and its duration.\n */\n recordToolExecution(tool: string, success: boolean, durationMs: number): void {\n this.incrementCounter(\"opensentinel_tool_executions_total\", {\n tool,\n success: String(success),\n });\n this.observeHistogram(\"opensentinel_tool_duration_ms\", durationMs);\n }\n\n /**\n * Record overall response latency.\n */\n recordLatency(durationMs: number): void {\n this.observeHistogram(\"opensentinel_response_latency_ms\", durationMs);\n }\n\n // -----------------------------------------------------------------------\n // Prometheus text exposition output\n // -----------------------------------------------------------------------\n\n /**\n * Produce the full metrics payload in Prometheus text exposition format\n * (`text/plain; version=0.0.4`).\n */\n toTextFormat(): string {\n const lines: string[] = [];\n\n // -- Counters ----------------------------------------------------------\n for (const def of COUNTER_DEFINITIONS) {\n lines.push(`# HELP ${def.name} ${def.help}`);\n lines.push(`# TYPE ${def.name} counter`);\n\n const counterMap = this.counters.get(def.name);\n if (counterMap) {\n for (const entry of counterMap.values()) {\n lines.push(`${def.name}${formatLabels(entry.labels)} ${entry.value}`);\n }\n }\n\n lines.push(\"\"); // blank line between metric families\n }\n\n // -- Histograms --------------------------------------------------------\n for (const def of HISTOGRAM_DEFINITIONS) {\n lines.push(`# HELP ${def.name} ${def.help}`);\n lines.push(`# TYPE ${def.name} histogram`);\n\n const observations = this.histograms.get(def.name) ?? [];\n const count = observations.length;\n const sum = observations.reduce((a, b) => a + b, 0);\n\n // Bucket lines\n for (const le of def.buckets) {\n const bucketCount = observations.filter((v) => v <= le).length;\n lines.push(`${def.name}_bucket{le=\"${le}\"} ${bucketCount}`);\n }\n lines.push(`${def.name}_bucket{le=\"+Inf\"} ${count}`);\n\n lines.push(`${def.name}_sum ${sum}`);\n lines.push(`${def.name}_count ${count}`);\n\n lines.push(\"\");\n }\n\n // -- Gauges ------------------------------------------------------------\n for (const def of GAUGE_DEFINITIONS) {\n lines.push(`# HELP ${def.name} ${def.help}`);\n lines.push(`# TYPE ${def.name} gauge`);\n\n if (def.name === \"opensentinel_uptime_seconds\") {\n const uptimeSeconds = (Date.now() - this.startTime) / 1000;\n lines.push(`${def.name} ${uptimeSeconds}`);\n } else if (def.name === \"opensentinel_memory_heap_bytes\") {\n const heapUsed = process.memoryUsage().heapUsed;\n lines.push(`${def.name} ${heapUsed}`);\n }\n\n lines.push(\"\");\n }\n\n return lines.join(\"\\n\");\n }\n\n // -----------------------------------------------------------------------\n // Reset\n // -----------------------------------------------------------------------\n\n /**\n * Reset all metrics to their initial state.\n */\n reset(): void {\n for (const def of COUNTER_DEFINITIONS) {\n this.counters.set(def.name, new Map());\n }\n for (const def of HISTOGRAM_DEFINITIONS) {\n this.histograms.set(def.name, []);\n }\n this.startTime = Date.now();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Singleton\n// ---------------------------------------------------------------------------\n\nexport const prometheusExporter = new PrometheusExporter();\n"],"mappings":"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","names":[]}
@@ -0,0 +1,103 @@
1
+ // src/tools/clipboard-manager.ts
2
+ var clipboard = /* @__PURE__ */ new Map();
3
+ var history = [];
4
+ var MAX_HISTORY = 100;
5
+ function detectType(content) {
6
+ const trimmed = content.trim();
7
+ if (/^https?:\/\//.test(trimmed)) return "url";
8
+ try {
9
+ JSON.parse(trimmed);
10
+ return "json";
11
+ } catch {
12
+ }
13
+ if (/^(function|const|let|var|class|import|export|def |public |private )/.test(trimmed)) return "code";
14
+ return "text";
15
+ }
16
+ function save(name, content, type) {
17
+ const entry = {
18
+ name,
19
+ content,
20
+ type: type || detectType(content),
21
+ createdAt: /* @__PURE__ */ new Date(),
22
+ accessCount: 0
23
+ };
24
+ clipboard.set(name, entry);
25
+ history.unshift({ ...entry });
26
+ if (history.length > MAX_HISTORY) {
27
+ history.length = MAX_HISTORY;
28
+ }
29
+ return { success: true, entry };
30
+ }
31
+ function get(name) {
32
+ const entry = clipboard.get(name);
33
+ if (!entry) {
34
+ return { success: false, error: `Clipboard entry '${name}' not found` };
35
+ }
36
+ entry.accessCount++;
37
+ return { success: true, entry: { ...entry } };
38
+ }
39
+ function remove(name) {
40
+ if (!clipboard.has(name)) {
41
+ return { success: false, error: `Clipboard entry '${name}' not found` };
42
+ }
43
+ clipboard.delete(name);
44
+ return { success: true };
45
+ }
46
+ function list() {
47
+ const entries = Array.from(clipboard.values()).map((e) => ({ ...e }));
48
+ return { success: true, entries };
49
+ }
50
+ function search(query) {
51
+ const lower = query.toLowerCase();
52
+ const matches = Array.from(clipboard.values()).filter(
53
+ (e) => e.name.toLowerCase().includes(lower) || e.content.toLowerCase().includes(lower)
54
+ ).map((e) => ({ ...e }));
55
+ return { success: true, entries: matches };
56
+ }
57
+ function getHistory(limit = 20) {
58
+ return { success: true, entries: history.slice(0, limit).map((e) => ({ ...e })) };
59
+ }
60
+ function clearAll() {
61
+ clipboard.clear();
62
+ history.length = 0;
63
+ return { success: true };
64
+ }
65
+ async function clipboardTool(action, name, content, options) {
66
+ try {
67
+ switch (action) {
68
+ case "save":
69
+ if (!content) return { success: false, error: "Content is required for save" };
70
+ return save(name, content, options?.type);
71
+ case "get":
72
+ return get(name);
73
+ case "delete":
74
+ return remove(name);
75
+ case "list":
76
+ return list();
77
+ case "search":
78
+ return search(name);
79
+ case "history":
80
+ return getHistory(options?.limit || 20);
81
+ case "clear":
82
+ return clearAll();
83
+ default:
84
+ return { success: false, error: `Unknown action: ${action}` };
85
+ }
86
+ } catch (error) {
87
+ return { success: false, error: error instanceof Error ? error.message : String(error) };
88
+ }
89
+ }
90
+ var clipboard_manager_default = { clipboardTool, save, get, remove, list, search, getHistory, clearAll };
91
+
92
+ export {
93
+ save,
94
+ get,
95
+ remove,
96
+ list,
97
+ search,
98
+ getHistory,
99
+ clearAll,
100
+ clipboardTool,
101
+ clipboard_manager_default
102
+ };
103
+ //# sourceMappingURL=chunk-MGFBLVR7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tools/clipboard-manager.ts"],"sourcesContent":["/**\r\n * Clipboard Manager\r\n * Named clipboard entries with history and search\r\n */\r\n\r\nexport interface ClipboardEntry {\r\n name: string;\r\n content: string;\r\n type: \"text\" | \"code\" | \"url\" | \"json\" | \"other\";\r\n createdAt: Date;\r\n accessCount: number;\r\n}\r\n\r\nexport interface ClipboardResult {\r\n success: boolean;\r\n entry?: ClipboardEntry;\r\n entries?: ClipboardEntry[];\r\n error?: string;\r\n}\r\n\r\n// In-memory clipboard store\r\nconst clipboard = new Map<string, ClipboardEntry>();\r\nconst history: ClipboardEntry[] = [];\r\nconst MAX_HISTORY = 100;\r\n\r\n// Detect content type\r\nfunction detectType(content: string): ClipboardEntry[\"type\"] {\r\n const trimmed = content.trim();\r\n if (/^https?:\\/\\//.test(trimmed)) return \"url\";\r\n try {\r\n JSON.parse(trimmed);\r\n return \"json\";\r\n } catch {}\r\n if (/^(function|const|let|var|class|import|export|def |public |private )/.test(trimmed)) return \"code\";\r\n return \"text\";\r\n}\r\n\r\n// Save content to a named clipboard entry\r\nexport function save(name: string, content: string, type?: ClipboardEntry[\"type\"]): ClipboardResult {\r\n const entry: ClipboardEntry = {\r\n name,\r\n content,\r\n type: type || detectType(content),\r\n createdAt: new Date(),\r\n accessCount: 0,\r\n };\r\n\r\n clipboard.set(name, entry);\r\n\r\n // Add to history\r\n history.unshift({ ...entry });\r\n if (history.length > MAX_HISTORY) {\r\n history.length = MAX_HISTORY;\r\n }\r\n\r\n return { success: true, entry };\r\n}\r\n\r\n// Get content from a named clipboard entry\r\nexport function get(name: string): ClipboardResult {\r\n const entry = clipboard.get(name);\r\n if (!entry) {\r\n return { success: false, error: `Clipboard entry '${name}' not found` };\r\n }\r\n\r\n entry.accessCount++;\r\n return { success: true, entry: { ...entry } };\r\n}\r\n\r\n// Delete a named clipboard entry\r\nexport function remove(name: string): ClipboardResult {\r\n if (!clipboard.has(name)) {\r\n return { success: false, error: `Clipboard entry '${name}' not found` };\r\n }\r\n\r\n clipboard.delete(name);\r\n return { success: true };\r\n}\r\n\r\n// List all clipboard entries\r\nexport function list(): ClipboardResult {\r\n const entries = Array.from(clipboard.values()).map((e) => ({ ...e }));\r\n return { success: true, entries };\r\n}\r\n\r\n// Search clipboard entries by content or name\r\nexport function search(query: string): ClipboardResult {\r\n const lower = query.toLowerCase();\r\n const matches = Array.from(clipboard.values())\r\n .filter(\r\n (e) =>\r\n e.name.toLowerCase().includes(lower) ||\r\n e.content.toLowerCase().includes(lower)\r\n )\r\n .map((e) => ({ ...e }));\r\n\r\n return { success: true, entries: matches };\r\n}\r\n\r\n// Get clipboard history\r\nexport function getHistory(limit: number = 20): ClipboardResult {\r\n return { success: true, entries: history.slice(0, limit).map((e) => ({ ...e })) };\r\n}\r\n\r\n// Clear all clipboard entries\r\nexport function clearAll(): ClipboardResult {\r\n clipboard.clear();\r\n history.length = 0;\r\n return { success: true };\r\n}\r\n\r\n// Main entry point\r\nexport async function clipboardTool(\r\n action: string,\r\n name: string,\r\n content?: string,\r\n options?: Record<string, unknown>\r\n): Promise<ClipboardResult> {\r\n try {\r\n switch (action) {\r\n case \"save\":\r\n if (!content) return { success: false, error: \"Content is required for save\" };\r\n return save(name, content, options?.type as any);\r\n case \"get\":\r\n return get(name);\r\n case \"delete\":\r\n return remove(name);\r\n case \"list\":\r\n return list();\r\n case \"search\":\r\n return search(name);\r\n case \"history\":\r\n return getHistory((options?.limit as number) || 20);\r\n case \"clear\":\r\n return clearAll();\r\n default:\r\n return { success: false, error: `Unknown action: ${action}` };\r\n }\r\n } catch (error) {\r\n return { success: false, error: error instanceof Error ? error.message : String(error) };\r\n }\r\n}\r\n\r\nexport default { clipboardTool, save, get, remove, list, search, getHistory, clearAll };\r\n"],"mappings":"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","names":[]}
@@ -0,0 +1,228 @@
1
+ import {
2
+ createBlockObject,
3
+ markdownToBlocks
4
+ } from "./chunk-TVEWKIK3.js";
5
+ import {
6
+ getNotionClient,
7
+ getRootPageId,
8
+ init_client
9
+ } from "./chunk-VRD5CYRL.js";
10
+
11
+ // src/integrations/notion/pages.ts
12
+ init_client();
13
+ function extractTitle(properties) {
14
+ for (const [key, value] of Object.entries(properties)) {
15
+ if (value?.type === "title" && Array.isArray(value.title)) {
16
+ return value.title.map((t) => t.plain_text || "").join("");
17
+ }
18
+ }
19
+ return "Untitled";
20
+ }
21
+ function pageToResult(page) {
22
+ return {
23
+ id: page.id,
24
+ url: page.url,
25
+ title: extractTitle(page.properties),
26
+ createdTime: page.created_time,
27
+ lastEditedTime: page.last_edited_time,
28
+ archived: page.archived,
29
+ icon: page.icon?.type === "emoji" ? page.icon.emoji : page.icon?.type === "external" ? page.icon.external.url : void 0,
30
+ cover: page.cover?.type === "external" ? page.cover.external.url : page.cover?.type === "file" ? page.cover.file.url : void 0,
31
+ properties: page.properties
32
+ };
33
+ }
34
+ async function createPage(options) {
35
+ const notion = getNotionClient();
36
+ let parent;
37
+ if (options.parentDatabaseId) {
38
+ parent = { database_id: options.parentDatabaseId };
39
+ } else if (options.parentPageId) {
40
+ parent = { page_id: options.parentPageId };
41
+ } else {
42
+ const rootId = getRootPageId();
43
+ if (!rootId) {
44
+ throw new Error("No parent specified and no root page configured");
45
+ }
46
+ parent = { page_id: rootId };
47
+ }
48
+ const properties = {
49
+ title: {
50
+ title: [
51
+ {
52
+ text: { content: options.title }
53
+ }
54
+ ]
55
+ },
56
+ ...buildProperties(options.properties)
57
+ };
58
+ let children;
59
+ if (options.content) {
60
+ const blocks = typeof options.content === "string" ? markdownToBlocks(options.content) : options.content;
61
+ children = blocks.map(createBlockObject);
62
+ }
63
+ const params = {
64
+ parent,
65
+ properties,
66
+ children
67
+ };
68
+ if (options.icon) {
69
+ if (options.icon.startsWith("http")) {
70
+ params.icon = { type: "external", external: { url: options.icon } };
71
+ } else {
72
+ params.icon = { type: "emoji", emoji: options.icon };
73
+ }
74
+ }
75
+ if (options.cover) {
76
+ params.cover = { type: "external", external: { url: options.cover } };
77
+ }
78
+ const response = await notion.pages.create(params);
79
+ return pageToResult(response);
80
+ }
81
+ async function getPage(pageId) {
82
+ const notion = getNotionClient();
83
+ const response = await notion.pages.retrieve({ page_id: pageId });
84
+ if (!("properties" in response)) {
85
+ throw new Error("Partial page object returned - cannot extract details");
86
+ }
87
+ return pageToResult(response);
88
+ }
89
+ async function updatePage(pageId, options) {
90
+ const notion = getNotionClient();
91
+ const params = {
92
+ page_id: pageId
93
+ };
94
+ if (options.title !== void 0) {
95
+ params.properties = {
96
+ ...params.properties,
97
+ title: {
98
+ title: [
99
+ {
100
+ text: { content: options.title }
101
+ }
102
+ ]
103
+ }
104
+ };
105
+ }
106
+ if (options.properties) {
107
+ params.properties = {
108
+ ...params.properties,
109
+ ...buildProperties(options.properties)
110
+ };
111
+ }
112
+ if (options.icon !== void 0) {
113
+ if (options.icon === null || options.icon === "") {
114
+ params.icon = null;
115
+ } else if (options.icon.startsWith("http")) {
116
+ params.icon = { type: "external", external: { url: options.icon } };
117
+ } else {
118
+ params.icon = { type: "emoji", emoji: options.icon };
119
+ }
120
+ }
121
+ if (options.cover !== void 0) {
122
+ if (options.cover === null || options.cover === "") {
123
+ params.cover = null;
124
+ } else {
125
+ params.cover = { type: "external", external: { url: options.cover } };
126
+ }
127
+ }
128
+ if (options.archived !== void 0) {
129
+ params.archived = options.archived;
130
+ }
131
+ const response = await notion.pages.update(params);
132
+ return pageToResult(response);
133
+ }
134
+ async function archivePage(pageId) {
135
+ return updatePage(pageId, { archived: true });
136
+ }
137
+ async function restorePage(pageId) {
138
+ return updatePage(pageId, { archived: false });
139
+ }
140
+ async function deletePage(pageId) {
141
+ await archivePage(pageId);
142
+ }
143
+ async function duplicatePage(pageId, newTitle, parentPageId) {
144
+ const notion = getNotionClient();
145
+ const originalPage = await getPage(pageId);
146
+ const blocksResponse = await notion.blocks.children.list({
147
+ block_id: pageId
148
+ });
149
+ const newPage = await createPage({
150
+ parentPageId,
151
+ title: newTitle || `${originalPage.title} (Copy)`,
152
+ icon: originalPage.icon,
153
+ cover: originalPage.cover
154
+ });
155
+ if (blocksResponse.results.length > 0) {
156
+ const blocksToCopy = [];
157
+ for (const block of blocksResponse.results) {
158
+ if ("type" in block) {
159
+ const blockCopy = { type: block.type };
160
+ blockCopy[block.type] = block[block.type];
161
+ blocksToCopy.push(blockCopy);
162
+ }
163
+ }
164
+ if (blocksToCopy.length > 0) {
165
+ await notion.blocks.children.append({
166
+ block_id: newPage.id,
167
+ children: blocksToCopy
168
+ });
169
+ }
170
+ }
171
+ return newPage;
172
+ }
173
+ function buildProperties(props) {
174
+ if (!props) return void 0;
175
+ const result = {};
176
+ for (const [key, value] of Object.entries(props)) {
177
+ if (key === "title") continue;
178
+ if (value === void 0) continue;
179
+ if (typeof value === "string") {
180
+ result[key] = {
181
+ rich_text: [{ text: { content: value } }]
182
+ };
183
+ } else if (typeof value === "number") {
184
+ result[key] = { number: value };
185
+ } else if (typeof value === "boolean") {
186
+ result[key] = { checkbox: value };
187
+ } else if (value instanceof Date) {
188
+ result[key] = { date: { start: value.toISOString() } };
189
+ } else if (Array.isArray(value)) {
190
+ result[key] = {
191
+ multi_select: value.map((v) => ({ name: v }))
192
+ };
193
+ }
194
+ }
195
+ return Object.keys(result).length > 0 ? result : void 0;
196
+ }
197
+ async function appendToPage(pageId, content) {
198
+ const notion = getNotionClient();
199
+ const blocks = typeof content === "string" ? markdownToBlocks(content) : content;
200
+ const blockObjects = blocks.map(createBlockObject);
201
+ await notion.blocks.children.append({
202
+ block_id: pageId,
203
+ children: blockObjects
204
+ });
205
+ }
206
+ async function replacePageContent(pageId, content) {
207
+ const notion = getNotionClient();
208
+ const existingBlocks = await notion.blocks.children.list({
209
+ block_id: pageId
210
+ });
211
+ for (const block of existingBlocks.results) {
212
+ await notion.blocks.delete({ block_id: block.id });
213
+ }
214
+ await appendToPage(pageId, content);
215
+ }
216
+
217
+ export {
218
+ createPage,
219
+ getPage,
220
+ updatePage,
221
+ archivePage,
222
+ restorePage,
223
+ deletePage,
224
+ duplicatePage,
225
+ appendToPage,
226
+ replacePageContent
227
+ };
228
+ //# sourceMappingURL=chunk-MQJ2ECQT.js.map