@datasynx/agentic-crm 0.1.0 → 1.1.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 (309) hide show
  1. package/README.md +270 -669
  2. package/dist/{approvals-DpjxGHFp.js → approvals-CmDT2eUg.js} +7 -24
  3. package/dist/approvals-CmDT2eUg.js.map +1 -0
  4. package/dist/{ask-CID3jnuL.js → ask-CDysGnRg.js} +6 -6
  5. package/dist/{ask-CID3jnuL.js.map → ask-CDysGnRg.js.map} +1 -1
  6. package/dist/atomic-write-8yjqqLtS.js +29 -0
  7. package/dist/atomic-write-8yjqqLtS.js.map +1 -0
  8. package/dist/atomic-write-BYmF-ThH.cjs +37 -0
  9. package/dist/atomic-write-BYmF-ThH.cjs.map +1 -0
  10. package/dist/attachments-CX2GAtsw.cjs +517 -0
  11. package/dist/attachments-CX2GAtsw.cjs.map +1 -0
  12. package/dist/attachments-D207gXfN.js +514 -0
  13. package/dist/attachments-D207gXfN.js.map +1 -0
  14. package/dist/attachments-rLa96rOK.js +514 -0
  15. package/dist/attachments-rLa96rOK.js.map +1 -0
  16. package/dist/auth-B5DcjJ_6.js +2 -0
  17. package/dist/{auth-DFWwWcYD.js → auth-DDXZTwS0.js} +4 -13
  18. package/dist/auth-DDXZTwS0.js.map +1 -0
  19. package/dist/{autofill-Di_-SP7t.js → autofill-B9VtlR2j.js} +2 -2
  20. package/dist/{autofill-Di_-SP7t.js.map → autofill-B9VtlR2j.js.map} +1 -1
  21. package/dist/{backup-CeMk9z86.js → backup-CTlIxUdO.js} +5 -7
  22. package/dist/backup-CTlIxUdO.js.map +1 -0
  23. package/dist/backup-LFnC09oV.js +2 -0
  24. package/dist/chunk-BfDYWZQ8.cjs +32 -0
  25. package/dist/chunk-BfDYWZQ8.cjs.map +1 -0
  26. package/dist/chunk-BhUZmQg5.js +32 -0
  27. package/dist/chunk-BhUZmQg5.js.map +1 -0
  28. package/dist/chunk-ChC83jai.js +2 -0
  29. package/dist/chunk-e_w8qqtP.js +32 -0
  30. package/dist/chunk-e_w8qqtP.js.map +1 -0
  31. package/dist/{churn-C28IgnAj.js → churn-DN9WDGNM.js} +3 -3
  32. package/dist/{churn-C28IgnAj.js.map → churn-DN9WDGNM.js.map} +1 -1
  33. package/dist/cli.js +285 -186
  34. package/dist/cli.js.map +1 -1
  35. package/dist/{compliance-CKSBoQUe.js → compliance-Bc12Hn9a.js} +3 -3
  36. package/dist/{compliance-CKSBoQUe.js.map → compliance-Bc12Hn9a.js.map} +1 -1
  37. package/dist/{compliance-CujOqAKk.js → compliance-TqYQXhBj.js} +1 -1
  38. package/dist/{compliance-B1kk5-YS.js → compliance-kq0xHRw3.js} +3 -3
  39. package/dist/{compliance-B1kk5-YS.js.map → compliance-kq0xHRw3.js.map} +1 -1
  40. package/dist/{compliance-B91zNvCR.cjs → compliance-pAj9FcGI.cjs} +3 -3
  41. package/dist/{compliance-B91zNvCR.cjs.map → compliance-pAj9FcGI.cjs.map} +1 -1
  42. package/dist/{context-builder-BzWAp3Zs.js → context-builder-7Uab5-G4.js} +3 -2
  43. package/dist/context-builder-7Uab5-G4.js.map +1 -0
  44. package/dist/context-builder-hmOPvgso.js +2 -0
  45. package/dist/{custom-fields-CzNeD3_v.js → custom-fields-BMyz5Ruh.js} +1 -1
  46. package/dist/{custom-fields-Pl2t9xzp.js → custom-fields-GzpOHW_2.js} +4 -13
  47. package/dist/custom-fields-GzpOHW_2.js.map +1 -0
  48. package/dist/{custom-objects-CIFrmQ2V.js → custom-objects-BNy-ayR-.js} +1 -1
  49. package/dist/{custom-objects-BHgn1GEX.js → custom-objects-CxW1gHwJ.js} +10 -25
  50. package/dist/custom-objects-CxW1gHwJ.js.map +1 -0
  51. package/dist/{customer-dir-DIylZ8Q6.js → customer-dir-CkMMXhb0.js} +9 -4
  52. package/dist/customer-dir-CkMMXhb0.js.map +1 -0
  53. package/dist/daemon/worker.js +66 -40
  54. package/dist/daemon/worker.js.map +1 -1
  55. package/dist/doctor-C14-vnJ1.js +103 -0
  56. package/dist/doctor-C14-vnJ1.js.map +1 -0
  57. package/dist/email-body-BFSRa0AW.cjs +42 -0
  58. package/dist/email-body-BFSRa0AW.cjs.map +1 -0
  59. package/dist/email-body-BOd7U-D2.js +42 -0
  60. package/dist/email-body-BOd7U-D2.js.map +1 -0
  61. package/dist/{enrichment-3XvgGDfB.js → enrichment-CDFdWmvD.js} +3 -3
  62. package/dist/{enrichment-3XvgGDfB.js.map → enrichment-CDFdWmvD.js.map} +1 -1
  63. package/dist/{file-lock-B_zi7NQl.js → file-lock-CcHotQkZ.js} +3 -4
  64. package/dist/file-lock-CcHotQkZ.js.map +1 -0
  65. package/dist/{gmail-sync-DIaxInDT.js → gmail-sync-B4Iu3AQb.js} +56 -22
  66. package/dist/gmail-sync-B4Iu3AQb.js.map +1 -0
  67. package/dist/{gmail-sync-hHm9gaWd.cjs → gmail-sync-BpSVESSe.cjs} +55 -21
  68. package/dist/gmail-sync-BpSVESSe.cjs.map +1 -0
  69. package/dist/{gmail-sync-rQaVqKWd.js → gmail-sync-DIbrPnTK.js} +55 -21
  70. package/dist/gmail-sync-DIbrPnTK.js.map +1 -0
  71. package/dist/{gmail-webhook-handler-e5Od25FX.js → gmail-webhook-handler-BzOFbvgh.js} +4 -4
  72. package/dist/{gmail-webhook-handler-e5Od25FX.js.map → gmail-webhook-handler-BzOFbvgh.js.map} +1 -1
  73. package/dist/{gmail-webhook-handler-DS7OlRPX.js → gmail-webhook-handler-CvSDW_Js.js} +2 -2
  74. package/dist/{goal-engine-KpBftn4V.js → goal-engine-BbroPhqm.js} +10 -11
  75. package/dist/goal-engine-BbroPhqm.js.map +1 -0
  76. package/dist/{goal-engine-CUZSpERI.js → goal-engine-CfDAJTFt.js} +1 -1
  77. package/dist/{google-drive-sync-DEPcqFca.js → google-drive-sync-B_I1d54Y.js} +3 -3
  78. package/dist/{google-drive-sync-DEPcqFca.js.map → google-drive-sync-B_I1d54Y.js.map} +1 -1
  79. package/dist/html-BaeOCZKE.js +36 -0
  80. package/dist/html-BaeOCZKE.js.map +1 -0
  81. package/dist/html-CmOku6jS.cjs +47 -0
  82. package/dist/html-CmOku6jS.cjs.map +1 -0
  83. package/dist/{hygiene-DZqfYpFf.js → hygiene-DzQPnc6P.js} +3 -3
  84. package/dist/{hygiene-DZqfYpFf.js.map → hygiene-DzQPnc6P.js.map} +1 -1
  85. package/dist/identity-CB7j-Zr1.js +2 -0
  86. package/dist/{identity-CI6olMNm.js → identity-_uZ3Lbr2.js} +2 -2
  87. package/dist/{identity-CI6olMNm.js.map → identity-_uZ3Lbr2.js.map} +1 -1
  88. package/dist/{import-hubspot-BaK71U_K.js → import-hubspot-CTId9IGV.js} +51 -45
  89. package/dist/import-hubspot-CTId9IGV.js.map +1 -0
  90. package/dist/{index-YqwMd6aQ.d.cts → index-BAutNcAT.d.cts} +20 -12
  91. package/dist/index-BAutNcAT.d.cts.map +1 -0
  92. package/dist/{index-V8BFaH-b.d.ts → index-FzDsNSSb.d.ts} +12 -4
  93. package/dist/index-FzDsNSSb.d.ts.map +1 -0
  94. package/dist/index.cjs +19 -21
  95. package/dist/index.cjs.map +1 -1
  96. package/dist/index.d.cts +20 -12
  97. package/dist/index.d.cts.map +1 -1
  98. package/dist/index.d.ts +12 -4
  99. package/dist/index.d.ts.map +1 -1
  100. package/dist/index.js +19 -21
  101. package/dist/index.js.map +1 -1
  102. package/dist/interactions-writer-B2y-73lh.js +2 -0
  103. package/dist/{interactions-writer-SLHnoEeE.js → interactions-writer-B8XAzdqR.js} +34 -4
  104. package/dist/interactions-writer-B8XAzdqR.js.map +1 -0
  105. package/dist/{interactions-writer-CrPStUll.cjs → interactions-writer-BRJNrefF.cjs} +7 -3
  106. package/dist/interactions-writer-BRJNrefF.cjs.map +1 -0
  107. package/dist/{interactions-writer-DO3KcSR3.js → interactions-writer-ZQcpFOh9.js} +7 -3
  108. package/dist/interactions-writer-ZQcpFOh9.js.map +1 -0
  109. package/dist/json-store-WWsFzXub.js +43 -0
  110. package/dist/json-store-WWsFzXub.js.map +1 -0
  111. package/dist/{knowledge-base-D0Fh40kc.js → knowledge-base--063Kpa3.js} +51 -22
  112. package/dist/knowledge-base--063Kpa3.js.map +1 -0
  113. package/dist/{lancedb-CCBbpulq.js → lancedb-CswQEE5K.js} +1 -1
  114. package/dist/{lancedb-rlvWoPwl.js → lancedb-CuHKNsNZ.js} +4 -3
  115. package/dist/lancedb-CuHKNsNZ.js.map +1 -0
  116. package/dist/{lead-model-BCFzyktm.js → lead-model-CEmx7te7.js} +6 -14
  117. package/dist/lead-model-CEmx7te7.js.map +1 -0
  118. package/dist/{llm-Z8RIYkpF.js → llm-BnSUBisu.js} +2 -2
  119. package/dist/{llm-Z8RIYkpF.js.map → llm-BnSUBisu.js.map} +1 -1
  120. package/dist/{llm-iijeXmgq.cjs → llm-CXycmEl9.cjs} +2 -2
  121. package/dist/{llm-iijeXmgq.cjs.map → llm-CXycmEl9.cjs.map} +1 -1
  122. package/dist/{llm-DEjWcqmW.js → llm-DSX1-wFu.js} +1 -1
  123. package/dist/{llm-DvzZqva0.js → llm-PZzgPphl.js} +3 -3
  124. package/dist/{llm-DvzZqva0.js.map → llm-PZzgPphl.js.map} +1 -1
  125. package/dist/logger-BkInaGoV.cjs +167 -0
  126. package/dist/logger-BkInaGoV.cjs.map +1 -0
  127. package/dist/logger-Dyl4VcLO.js +147 -0
  128. package/dist/logger-Dyl4VcLO.js.map +1 -0
  129. package/dist/logger-UaF5p9d1.js +147 -0
  130. package/dist/logger-UaF5p9d1.js.map +1 -0
  131. package/dist/logger-vKQS34w9.js +2 -0
  132. package/dist/mcp-CdTJWTJf.d.cts.map +1 -1
  133. package/dist/mcp-CdTJWTJf.d.ts.map +1 -1
  134. package/dist/mcp.cjs +365 -319
  135. package/dist/mcp.cjs.map +1 -1
  136. package/dist/mcp.d.cts.map +1 -1
  137. package/dist/mcp.d.ts.map +1 -1
  138. package/dist/mcp.js +365 -319
  139. package/dist/mcp.js.map +1 -1
  140. package/dist/{memory-Cy6-Tbyl.js → memory-D8hmgD9d.js} +1 -1
  141. package/dist/{memory-Bb6ky3kb.js → memory-Dzr9dXSM.js} +4 -11
  142. package/dist/memory-Dzr9dXSM.js.map +1 -0
  143. package/dist/{microsoft-calendar-B6MMtUQK.js → microsoft-calendar-BgVR8GDv.js} +4 -4
  144. package/dist/{microsoft-calendar-B6MMtUQK.js.map → microsoft-calendar-BgVR8GDv.js.map} +1 -1
  145. package/dist/{microsoft-sync-CpZVoSuq.js → microsoft-sync-D30_XksI.js} +5 -5
  146. package/dist/{microsoft-sync-CpZVoSuq.js.map → microsoft-sync-D30_XksI.js.map} +1 -1
  147. package/dist/{nba-3wanmJ0U.js → nba-DwdfM93s.js} +3 -3
  148. package/dist/{nba-3wanmJ0U.js.map → nba-DwdfM93s.js.map} +1 -1
  149. package/dist/{notification-dispatcher-0vYNngWe.js → notification-dispatcher-inpKyuBz.js} +7 -3
  150. package/dist/notification-dispatcher-inpKyuBz.js.map +1 -0
  151. package/dist/{pipeline-writer-BqBrYrQc.js → pipeline-writer-0LJ6Qkat.js} +1 -1
  152. package/dist/{pipeline-writer-N2omexxp.cjs → pipeline-writer-B1tRAhuD.cjs} +11 -3
  153. package/dist/pipeline-writer-B1tRAhuD.cjs.map +1 -0
  154. package/dist/{pipeline-writer-BvVquKIe.js → pipeline-writer-CIllfnZl.js} +5 -3
  155. package/dist/pipeline-writer-CIllfnZl.js.map +1 -0
  156. package/dist/{pipeline-writer-eufx_0o1.js → pipeline-writer-rDj-ni6q.js} +6 -4
  157. package/dist/pipeline-writer-rDj-ni6q.js.map +1 -0
  158. package/dist/{proactive-agent-BgQXw3ac.js → proactive-agent-B7u3Bj_l.js} +6 -6
  159. package/dist/{proactive-agent-BgQXw3ac.js.map → proactive-agent-B7u3Bj_l.js.map} +1 -1
  160. package/dist/{proactive-worker-BrLHNhjH.js → proactive-worker-1zkm6aJD.js} +7 -8
  161. package/dist/proactive-worker-1zkm6aJD.js.map +1 -0
  162. package/dist/{push-manager-CowY-0IK.js → push-manager-BXM-IHfP.js} +1 -1
  163. package/dist/{push-manager-CdqIIkuh.js → push-manager-C0ECQgva.js} +4 -4
  164. package/dist/push-manager-C0ECQgva.js.map +1 -0
  165. package/dist/{quote-generator-OhSFsi3x.js → quote-generator-ByUyIYtw.js} +1 -1
  166. package/dist/{quote-generator-BfwENXzg.js → quote-generator-CTdR8eEI.js} +5 -5
  167. package/dist/quote-generator-CTdR8eEI.js.map +1 -0
  168. package/dist/rbac-DzbyFhVH.js +2 -0
  169. package/dist/{rbac-CTIktZaC.js → rbac-msmBc_tK.js} +19 -12
  170. package/dist/rbac-msmBc_tK.js.map +1 -0
  171. package/dist/regex-Jt5DatPi.js +13 -0
  172. package/dist/regex-Jt5DatPi.js.map +1 -0
  173. package/dist/{relationship-health-odxEoQdJ.js → relationship-health-ZZNXR1RZ.js} +8 -16
  174. package/dist/relationship-health-ZZNXR1RZ.js.map +1 -0
  175. package/dist/{revenue-simulation-Bqf2DLVB.js → revenue-simulation-D8f_YkUY.js} +9 -19
  176. package/dist/revenue-simulation-D8f_YkUY.js.map +1 -0
  177. package/dist/{revenue-simulation-BJdRTEHc.js → revenue-simulation-njJZlTqm.js} +1 -1
  178. package/dist/safe-path-mpp0dKtO.js +18 -0
  179. package/dist/safe-path-mpp0dKtO.js.map +1 -0
  180. package/dist/{segments-BqcD5HIl.js → segments-DI3LOQNe.js} +5 -14
  181. package/dist/segments-DI3LOQNe.js.map +1 -0
  182. package/dist/sequence-engine-C6nnewHX.js +2 -0
  183. package/dist/{sequence-engine-J1lTW_in.js → sequence-engine-DNTVLq7o.js} +15 -8
  184. package/dist/sequence-engine-DNTVLq7o.js.map +1 -0
  185. package/dist/{sequence-store-DaaWr0Os.js → sequence-store-CmYb6s0g.js} +6 -5
  186. package/dist/sequence-store-CmYb6s0g.js.map +1 -0
  187. package/dist/{server-Dyva03K8.js → server-DoRPPOeR.js} +308 -230
  188. package/dist/server-DoRPPOeR.js.map +1 -0
  189. package/dist/{session-D9ub6Wl1.js → session-B6XaP83h.js} +3 -3
  190. package/dist/session-B6XaP83h.js.map +1 -0
  191. package/dist/{session-B9AilxOE.js → session-BgGDyP2C.js} +3 -3
  192. package/dist/session-BgGDyP2C.js.map +1 -0
  193. package/dist/session-Bp4zTh4l.js +2 -0
  194. package/dist/{session-D0qFkBla.cjs → session-Mm7GQbSH.cjs} +3 -3
  195. package/dist/session-Mm7GQbSH.cjs.map +1 -0
  196. package/dist/{session-store-C8tEvMPw.js → session-store-DWxJ5Pof.js} +79 -17
  197. package/dist/session-store-DWxJ5Pof.js.map +1 -0
  198. package/dist/{session-store-B0QZE8Bx.cjs → session-store-yfwnj0OC.cjs} +126 -16
  199. package/dist/session-store-yfwnj0OC.cjs.map +1 -0
  200. package/dist/{sla-engine-5IhTsBUR.js → sla-engine-CP2KiKDS.js} +1 -1
  201. package/dist/{sla-engine-BqX-7u-7.js → sla-engine-O-A1ntu_.js} +2 -2
  202. package/dist/{sla-engine-BqX-7u-7.js.map → sla-engine-O-A1ntu_.js.map} +1 -1
  203. package/dist/{sop-Vp0UPWFW.js → sop-BV7ICAFR.js} +4 -11
  204. package/dist/sop-BV7ICAFR.js.map +1 -0
  205. package/dist/{sop-DkhVChGy.js → sop-D33qTHUb.js} +1 -1
  206. package/dist/survey-engine-DKctGcLQ.js +2 -0
  207. package/dist/{survey-engine-DBjCYqCv.js → survey-engine-DngXBv47.js} +5 -4
  208. package/dist/survey-engine-DngXBv47.js.map +1 -0
  209. package/dist/{sync-state-CwLSt_1m.js → sync-state-BaA8LbTI.js} +1 -1
  210. package/dist/{sync-state-ChaLbamC.js → sync-state-DMZgzpez.js} +4 -12
  211. package/dist/sync-state-DMZgzpez.js.map +1 -0
  212. package/dist/{ticket-writer-CjqKeIRD.js → ticket-writer-DsfpeLGZ.js} +1 -1
  213. package/dist/{ticket-writer-j2oX_Wal.js → ticket-writer-a9on36Wb.js} +12 -24
  214. package/dist/ticket-writer-a9on36Wb.js.map +1 -0
  215. package/dist/{tone-Bdm5uaht.js → tone-C7bqK69y.js} +5 -12
  216. package/dist/tone-C7bqK69y.js.map +1 -0
  217. package/dist/{tone-DRKlZgPr.cjs → tone-Cmc7O2Fx.cjs} +3 -9
  218. package/dist/tone-Cmc7O2Fx.cjs.map +1 -0
  219. package/dist/{tone-vNb2DAAD.js → tone-mXSftvTn.js} +3 -8
  220. package/dist/tone-mXSftvTn.js.map +1 -0
  221. package/dist/{transcript-watcher-CL2QUygI.js → transcript-watcher-BoClrJAz.js} +18 -11
  222. package/dist/transcript-watcher-BoClrJAz.js.map +1 -0
  223. package/dist/unmatched-transcripts-C92zAoM4.js +2 -0
  224. package/dist/unmatched-transcripts-DC-VQ9YS.js +16 -0
  225. package/dist/unmatched-transcripts-DC-VQ9YS.js.map +1 -0
  226. package/dist/update-deal-CWy1eLJI.js +2 -0
  227. package/dist/{update-deal-DKC79skb.js → update-deal-DSzr_Aau.js} +3 -3
  228. package/dist/{update-deal-DKC79skb.js.map → update-deal-DSzr_Aau.js.map} +1 -1
  229. package/dist/{usage-D0-TYJkw.js → usage-BVlFlKW_.js} +8 -6
  230. package/dist/usage-BVlFlKW_.js.map +1 -0
  231. package/dist/usage-CClTf5e6.cjs.map +1 -1
  232. package/dist/usage-D0u9a-lV.js.map +1 -1
  233. package/dist/{vault-DXCg29W-.js → vault-CfwZdNzC.js} +3 -4
  234. package/dist/vault-CfwZdNzC.js.map +1 -0
  235. package/dist/{vault-C1D3zScD.js → vault-DxKP4_R2.js} +1 -1
  236. package/dist/{webhooks-Xn6zO6kd.cjs → webhooks-CwW-3kvG.cjs} +5 -19
  237. package/dist/webhooks-CwW-3kvG.cjs.map +1 -0
  238. package/dist/{webhooks-7EpA05Qr.js → webhooks-DXr1IoKn.js} +8 -21
  239. package/dist/webhooks-DXr1IoKn.js.map +1 -0
  240. package/dist/{webhooks-BO2UAnmn.js → webhooks-sWZ8CJtR.js} +5 -18
  241. package/dist/webhooks-sWZ8CJtR.js.map +1 -0
  242. package/package.json +22 -2
  243. package/dist/approvals-DpjxGHFp.js.map +0 -1
  244. package/dist/auth-CyFuu9X_.js +0 -2
  245. package/dist/auth-DFWwWcYD.js.map +0 -1
  246. package/dist/backup-CeMk9z86.js.map +0 -1
  247. package/dist/backup-f_hC7rBV.js +0 -2
  248. package/dist/context-builder-BzWAp3Zs.js.map +0 -1
  249. package/dist/context-builder-DlrRcqmJ.js +0 -2
  250. package/dist/custom-fields-Pl2t9xzp.js.map +0 -1
  251. package/dist/custom-objects-BHgn1GEX.js.map +0 -1
  252. package/dist/customer-dir-DIylZ8Q6.js.map +0 -1
  253. package/dist/file-lock-B_zi7NQl.js.map +0 -1
  254. package/dist/gmail-sync-DIaxInDT.js.map +0 -1
  255. package/dist/gmail-sync-hHm9gaWd.cjs.map +0 -1
  256. package/dist/gmail-sync-rQaVqKWd.js.map +0 -1
  257. package/dist/goal-engine-KpBftn4V.js.map +0 -1
  258. package/dist/identity-gyfWdrcX.js +0 -2
  259. package/dist/import-hubspot-BaK71U_K.js.map +0 -1
  260. package/dist/index-V8BFaH-b.d.ts.map +0 -1
  261. package/dist/index-YqwMd6aQ.d.cts.map +0 -1
  262. package/dist/interactions-writer-CrPStUll.cjs.map +0 -1
  263. package/dist/interactions-writer-DO3KcSR3.js.map +0 -1
  264. package/dist/interactions-writer-SLHnoEeE.js.map +0 -1
  265. package/dist/interactions-writer-dSPy1XfO.js +0 -2
  266. package/dist/knowledge-base-D0Fh40kc.js.map +0 -1
  267. package/dist/lancedb-rlvWoPwl.js.map +0 -1
  268. package/dist/lead-model-BCFzyktm.js.map +0 -1
  269. package/dist/memory-Bb6ky3kb.js.map +0 -1
  270. package/dist/notification-dispatcher-0vYNngWe.js.map +0 -1
  271. package/dist/pipeline-writer-BvVquKIe.js.map +0 -1
  272. package/dist/pipeline-writer-N2omexxp.cjs.map +0 -1
  273. package/dist/pipeline-writer-eufx_0o1.js.map +0 -1
  274. package/dist/proactive-worker-BrLHNhjH.js.map +0 -1
  275. package/dist/push-manager-CdqIIkuh.js.map +0 -1
  276. package/dist/quote-generator-BfwENXzg.js.map +0 -1
  277. package/dist/rbac-C7c8tcES.js +0 -2
  278. package/dist/rbac-CTIktZaC.js.map +0 -1
  279. package/dist/relationship-health-odxEoQdJ.js.map +0 -1
  280. package/dist/revenue-simulation-Bqf2DLVB.js.map +0 -1
  281. package/dist/segments-BqcD5HIl.js.map +0 -1
  282. package/dist/sequence-engine-CCTHEBgi.js +0 -2
  283. package/dist/sequence-engine-J1lTW_in.js.map +0 -1
  284. package/dist/sequence-store-DaaWr0Os.js.map +0 -1
  285. package/dist/server-Dyva03K8.js.map +0 -1
  286. package/dist/session-B9AilxOE.js.map +0 -1
  287. package/dist/session-D0qFkBla.cjs.map +0 -1
  288. package/dist/session-D9ub6Wl1.js.map +0 -1
  289. package/dist/session-mWHA71Lw.js +0 -2
  290. package/dist/session-store-B0QZE8Bx.cjs.map +0 -1
  291. package/dist/session-store-C8tEvMPw.js.map +0 -1
  292. package/dist/sop-Vp0UPWFW.js.map +0 -1
  293. package/dist/survey-engine-C06hcQt3.js +0 -2
  294. package/dist/survey-engine-DBjCYqCv.js.map +0 -1
  295. package/dist/sync-state-ChaLbamC.js.map +0 -1
  296. package/dist/ticket-writer-j2oX_Wal.js.map +0 -1
  297. package/dist/tone-Bdm5uaht.js.map +0 -1
  298. package/dist/tone-DRKlZgPr.cjs.map +0 -1
  299. package/dist/tone-vNb2DAAD.js.map +0 -1
  300. package/dist/transcript-watcher-CL2QUygI.js.map +0 -1
  301. package/dist/unmatched-transcripts-BsH5bhkU.js +0 -26
  302. package/dist/unmatched-transcripts-BsH5bhkU.js.map +0 -1
  303. package/dist/unmatched-transcripts-D0PrJ9iz.js +0 -2
  304. package/dist/update-deal-BNwPGaTV.js +0 -2
  305. package/dist/usage-D0-TYJkw.js.map +0 -1
  306. package/dist/vault-DXCg29W-.js.map +0 -1
  307. package/dist/webhooks-7EpA05Qr.js.map +0 -1
  308. package/dist/webhooks-BO2UAnmn.js.map +0 -1
  309. package/dist/webhooks-Xn6zO6kd.cjs.map +0 -1
package/dist/index.d.ts CHANGED
@@ -136,11 +136,13 @@ declare const InteractionEntrySchema: z.ZodObject<{
136
136
  subject: z.ZodOptional<z.ZodString>;
137
137
  summary: z.ZodString;
138
138
  nextSteps: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
139
+ /** Relative links (from the customer dir) to converted attachment Markdown. */
140
+ attachments: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
139
141
  sourceRef: z.ZodString;
140
142
  synced: z.ZodString;
141
143
  }, "strip", z.ZodTypeAny, {
142
- type: "Email" | "Call" | "Meeting" | "Note" | "Demo" | "Proposal" | "Contract" | "Other";
143
144
  date: string;
145
+ type: "Email" | "Call" | "Meeting" | "Note" | "Demo" | "Proposal" | "Contract" | "Other";
144
146
  with: string;
145
147
  summary: string;
146
148
  nextSteps: string[];
@@ -148,9 +150,10 @@ declare const InteractionEntrySchema: z.ZodObject<{
148
150
  synced: string;
149
151
  direction?: "inbound" | "outbound" | undefined;
150
152
  subject?: string | undefined;
153
+ attachments?: string[] | undefined;
151
154
  }, {
152
- type: "Email" | "Call" | "Meeting" | "Note" | "Demo" | "Proposal" | "Contract" | "Other";
153
155
  date: string;
156
+ type: "Email" | "Call" | "Meeting" | "Note" | "Demo" | "Proposal" | "Contract" | "Other";
154
157
  with: string;
155
158
  summary: string;
156
159
  sourceRef: string;
@@ -158,6 +161,7 @@ declare const InteractionEntrySchema: z.ZodObject<{
158
161
  direction?: "inbound" | "outbound" | undefined;
159
162
  subject?: string | undefined;
160
163
  nextSteps?: string[] | undefined;
164
+ attachments?: string[] | undefined;
161
165
  }>;
162
166
  type InteractionEntry = z.infer<typeof InteractionEntrySchema>;
163
167
  //# sourceMappingURL=interaction.d.ts.map
@@ -516,7 +520,11 @@ interface RbacConfig {
516
520
  declare function getRbacConfig(dataDir: string): RbacConfig;
517
521
  declare function getRole(dataDir: string, actor: string): Role;
518
522
  declare function canSeeCustomer(dataDir: string, actor: string, slug: string): boolean;
519
- /** Load the field-level ACL (field → allowed roles) from rbac.json. */
523
+ /**
524
+ * Build a once-loaded predicate for which customers `actor` may see. Equivalent
525
+ * to calling canSeeCustomer per slug, but reads/parses rbac.json a single time
526
+ * (and uses O(1) Set membership) — for hot loops like list_customers.
527
+ */
520
528
  //#endregion
521
529
  //#region src/core/session-store.d.ts
522
530
  interface Session {
@@ -536,4 +544,4 @@ declare const VERSION = "0.1.0";
536
544
 
537
545
  //#endregion
538
546
  export { type GlobalSources, type InteractionEntry, type KbArticle, type MainFacts, type PipelineDeal, type QuoteLineItem, type Quote as QuoteRecord, type SurveyDefinition, type SurveyResponse, type TicketPriority, type Ticket as TicketRecord, type TicketStatus, VERSION, canSeeCustomer, clearSession, createCustomer, customerExists, filterAuditLog, getRbacConfig, getRole, getSession, readAuditLog, readMainFacts, runAudit, runBackup, runValidate, setSession };
539
- //# sourceMappingURL=index-V8BFaH-b.d.ts.map
547
+ //# sourceMappingURL=index-FzDsNSSb.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-V8BFaH-b.d.ts","names":[],"sources":["../src/schemas/sources.ts","../src/schemas/main-facts.ts","../src/schemas/interaction.ts","../src/schemas/pipeline.ts","../src/schemas/ticket.ts","../src/schemas/quote.ts","../src/schemas/kb-article.ts","../src/schemas/survey.ts","../src/commands/create.ts","../src/commands/backup.ts","../src/commands/audit.ts","../src/commands/validate.ts","../src/fs/customer-dir.ts","../src/fs/audit-log.ts","../src/core/rbac.ts","../src/core/session-store.ts","../src/version.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index-FzDsNSSb.d.ts","names":[],"sources":["../src/schemas/sources.ts","../src/schemas/main-facts.ts","../src/schemas/interaction.ts","../src/schemas/pipeline.ts","../src/schemas/ticket.ts","../src/schemas/quote.ts","../src/schemas/kb-article.ts","../src/schemas/survey.ts","../src/commands/create.ts","../src/commands/backup.ts","../src/commands/audit.ts","../src/commands/validate.ts","../src/fs/customer-dir.ts","../src/fs/audit-log.ts","../src/core/rbac.ts","../src/core/session-store.ts","../src/version.ts"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
- import { C as readMainFacts, S as listCustomerSlugs, T as MainFactsSchema, _ as info, b as customerExists, c as filterAuditLog, g as error, h as bold, i as canSeeCustomer, m as runBackup, n as getSession, o as getRbacConfig, r as setSession, s as getRole, t as clearSession, u as readAuditLog, v as success, w as writeMainFacts, x as ensureCustomerDir, y as warning } from "./session-store-C8tEvMPw.js";
1
+ import { A as writeMainFacts, D as ensureCustomerDir, E as customerExists, N as MainFactsSchema, O as listCustomerSlugs, _ as error, b as warning, c as getRole, d as readAuditLog, g as bold, h as runBackup, i as canSeeCustomer, k as readMainFacts, l as filterAuditLog, n as getSession, r as setSession, s as getRbacConfig, t as clearSession, v as info, w as writeJsonFile, y as success } from "./session-store-DWxJ5Pof.js";
2
+ import { t as writeFileAtomic } from "./atomic-write-8yjqqLtS.js";
2
3
  import { Command } from "commander";
3
4
  import path from "path";
4
5
  import fs from "fs";
@@ -22,28 +23,25 @@ async function createCustomer(opts) {
22
23
  updated: today
23
24
  });
24
25
  const interactionsPath = path.join(dir, "interactions.md");
25
- if (!fs.existsSync(interactionsPath)) fs.writeFileSync(interactionsPath, `# Interactions — ${opts.name}\n\n`);
26
+ if (!fs.existsSync(interactionsPath)) writeFileAtomic(interactionsPath, `# Interactions — ${opts.name}\n\n`);
26
27
  const pipelinePath = path.join(dir, "pipeline.md");
27
- if (!fs.existsSync(pipelinePath)) fs.writeFileSync(pipelinePath, `# Pipeline — ${opts.name}\n\n| Deal | Stage | Value | Currency | Probability | Close Date | Updated | Notes |\n|---|---|---|---|---|---|---|---|\n`);
28
+ if (!fs.existsSync(pipelinePath)) writeFileAtomic(pipelinePath, `# Pipeline — ${opts.name}\n\n| Deal | Stage | Value | Currency | Probability | Close Date | Updated | Notes |\n|---|---|---|---|---|---|---|---|\n`);
28
29
  const sourcesPath = path.join(dir, "sources.json");
29
- if (!fs.existsSync(sourcesPath)) {
30
- const sources = {
31
- gmail: {
32
- type: "gmail",
33
- query: opts.domain ? `from:${opts.domain} OR to:${opts.domain}` : opts.email ? `from:${opts.email} OR to:${opts.email}` : "",
34
- enabled: true
35
- },
36
- version: 1,
37
- created: (/* @__PURE__ */ new Date()).toISOString()
38
- };
39
- fs.writeFileSync(sourcesPath, JSON.stringify(sources, null, 2));
40
- }
30
+ if (!fs.existsSync(sourcesPath)) writeJsonFile(sourcesPath, {
31
+ gmail: {
32
+ type: "gmail",
33
+ query: opts.domain ? `from:${opts.domain} OR to:${opts.domain}` : opts.email ? `from:${opts.email} OR to:${opts.email}` : "",
34
+ enabled: true
35
+ },
36
+ version: 1,
37
+ created: (/* @__PURE__ */ new Date()).toISOString()
38
+ });
41
39
  return {
42
40
  id,
43
41
  dir
44
42
  };
45
43
  }
46
- new Command("create").argument("<name>", "Customer name").option("--domain <domain>", "Primary domain (for Gmail sync)").option("--email <email>", "Primary contact email").action(async (name, opts) => {
44
+ new Command("create").description("Create a new customer").argument("<name>", "Customer name").option("--domain <domain>", "Primary domain (for Gmail sync)").option("--email <email>", "Primary contact email").action(async (name, opts) => {
47
45
  try {
48
46
  const { id, dir } = await createCustomer({
49
47
  name,
@@ -129,17 +127,17 @@ async function runValidate(opts, dataDir) {
129
127
  }
130
128
  try {
131
129
  let content = fs.readFileSync(factsPath, "utf-8");
132
- const { data } = matter(content);
130
+ let parsed = matter(content);
133
131
  if (opts.fix) {
134
- const result = applyFix(factsPath, content, data);
132
+ const result = applyFix(factsPath, content, parsed.data);
135
133
  if (result) {
136
134
  content = result.content;
135
+ parsed = matter(content);
137
136
  fixedCount++;
138
137
  console.log(info(`⚙ ${slug}: fixed ${result.fixed.join(", ")}`));
139
138
  }
140
139
  }
141
- const { data: refetchedData } = matter(content);
142
- MainFactsSchema.parse(refetchedData);
140
+ MainFactsSchema.parse(parsed.data);
143
141
  if (!fs.existsSync(interactionsPath)) console.log(warning(`⚠ ${slug}: missing interactions.md`));
144
142
  else console.log(success(`✓ ${slug}`));
145
143
  } catch (err) {
@@ -153,7 +151,7 @@ async function runValidate(opts, dataDir) {
153
151
  process.exit(1);
154
152
  } else console.log(success("\n✓ All customers valid."));
155
153
  }
156
- new Command("validate").option("--fix", "Auto-fix recoverable issues").action(async (opts) => {
154
+ new Command("validate").description("Validate all customer data against schemas").option("--fix", "Auto-fix recoverable issues").action(async (opts) => {
157
155
  await runValidate(opts, process.env["DXCRM_DATA_DIR"] ?? process.cwd());
158
156
  });
159
157
  //#endregion
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/commands/create.ts","../src/commands/audit.ts","../src/commands/validate.ts","../src/version.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport path from \"path\";\nimport fs from \"fs\";\nimport slugify from \"slug\";\nimport { ensureCustomerDir, writeMainFacts } from \"../fs/customer-dir.js\";\nimport { success, error, bold } from \"../ui/colors.js\";\n\nexport async function createCustomer(opts: {\n name: string;\n domain?: string;\n email?: string;\n dataDir?: string;\n}): Promise<{ id: string; dir: string }> {\n const id = slugify(opts.name, { lower: true });\n const dataDir = opts.dataDir ?? process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n await ensureCustomerDir(dataDir, id);\n const dir = path.join(dataDir, \"customers\", id);\n\n // Write main_facts.md\n const today = new Date().toISOString().slice(0, 10);\n await writeMainFacts(dataDir, id, {\n name: opts.name,\n domain: opts.domain,\n email: opts.email,\n relationship_stage: \"prospect\",\n tags: [],\n currency: \"EUR\",\n created: today,\n updated: today,\n });\n\n // Create interactions.md\n const interactionsPath = path.join(dir, \"interactions.md\");\n if (!fs.existsSync(interactionsPath)) {\n fs.writeFileSync(interactionsPath, `# Interactions — ${opts.name}\\n\\n`);\n }\n\n // Create pipeline.md\n const pipelinePath = path.join(dir, \"pipeline.md\");\n if (!fs.existsSync(pipelinePath)) {\n fs.writeFileSync(\n pipelinePath,\n `# Pipeline — ${opts.name}\\n\\n| Deal | Stage | Value | Currency | Probability | Close Date | Updated | Notes |\\n|---|---|---|---|---|---|---|---|\\n`\n );\n }\n\n // Create sources.json\n const sourcesPath = path.join(dir, \"sources.json\");\n if (!fs.existsSync(sourcesPath)) {\n const gmailQuery = opts.domain\n ? `from:${opts.domain} OR to:${opts.domain}`\n : opts.email\n ? `from:${opts.email} OR to:${opts.email}`\n : \"\";\n const sources = {\n gmail: {\n type: \"gmail\",\n query: gmailQuery,\n enabled: true,\n },\n version: 1,\n created: new Date().toISOString(),\n };\n fs.writeFileSync(sourcesPath, JSON.stringify(sources, null, 2));\n }\n\n return { id, dir };\n}\n\nexport const createCommand = new Command(\"create\")\n .argument(\"<name>\", \"Customer name\")\n .option(\"--domain <domain>\", \"Primary domain (for Gmail sync)\")\n .option(\"--email <email>\", \"Primary contact email\")\n .action(async (name: string, opts: { domain?: string; email?: string }) => {\n try {\n const { id, dir } = await createCustomer({ name, ...opts });\n console.log(success(`✓ Created customer: ${bold(id)}`));\n console.log(` Dir: ${dir}`);\n console.log(` Files: main_facts.md, interactions.md, pipeline.md, sources.json`);\n } catch (err) {\n console.error(error(`✗ ${(err as Error).message}`));\n process.exit(1);\n }\n });\n","import { Command } from \"commander\";\nimport { readAuditLog, filterAuditLog } from \"../fs/audit-log.js\";\n\nconst SEP = \"─\".repeat(70);\n\nexport async function runAudit(\n opts: {\n slug?: string;\n actor?: string;\n limit?: number;\n tail?: boolean;\n },\n dataDir?: string\n): Promise<void> {\n const dir = dataDir ?? process.cwd();\n const limit = opts.limit ?? 20;\n\n const allEntries = readAuditLog(dir);\n const entries = filterAuditLog(allEntries, {\n ...(opts.slug !== undefined ? { slug: opts.slug } : {}),\n ...(opts.actor !== undefined ? { actor: opts.actor } : {}),\n limit,\n });\n\n console.log(SEP);\n console.log(\" DatasynxOpenCRM — Audit Trail\");\n\n if (opts.slug) console.log(` Customer: ${opts.slug}`);\n if (opts.actor) console.log(` Actor: ${opts.actor}`);\n\n console.log(SEP);\n\n if (entries.length === 0) {\n console.log(\" No audit entries found.\");\n console.log(SEP);\n return;\n }\n\n for (const entry of entries) {\n console.log(\n ` ${entry.timestamp} ${entry.actor.padEnd(12)} ${entry.tool.padEnd(20)} ${entry.slug.padEnd(20)} ${entry.summary}`\n );\n }\n\n console.log(SEP);\n console.log(` ${entries.length} entr${entries.length === 1 ? \"y\" : \"ies\"} shown`);\n console.log(SEP);\n}\n\nexport const auditCommand = new Command(\"audit\")\n .description(\"Show CRM audit trail — who changed what and when\")\n .option(\"--slug <slug>\", \"Filter by customer slug\")\n .option(\"--actor <actor>\", \"Filter by actor\")\n .option(\"--limit <n>\", \"Number of entries to show (default: 20)\", parseInt)\n .option(\"--tail\", \"Show all new entries (simplified: shows current entries)\")\n .action((opts: { slug?: string; actor?: string; limit?: number; tail?: boolean }) =>\n runAudit(opts, process.env[\"DXCRM_DATA_DIR\"])\n );\n","import { Command } from \"commander\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { MainFactsSchema } from \"../schemas/main-facts.js\";\nimport { listCustomerSlugs } from \"../fs/customer-dir.js\";\nimport matter from \"gray-matter\";\nimport { success, error, warning, info } from \"../ui/colors.js\";\n\nconst RECOVERABLE_DEFAULTS: Record<string, unknown> = {\n tags: [],\n currency: \"EUR\",\n};\n\nexport function applyFix(\n factsPath: string,\n content: string,\n data: Record<string, unknown>\n): { fixed: string[]; content: string } | null {\n const fixed: string[] = [];\n const patched = { ...data };\n\n for (const [field, defaultValue] of Object.entries(RECOVERABLE_DEFAULTS)) {\n if (patched[field] === undefined || patched[field] === null) {\n patched[field] = defaultValue;\n fixed.push(`${field} → ${JSON.stringify(defaultValue)}`);\n }\n }\n\n if (patched[\"updated\"] === undefined && patched[\"created\"]) {\n patched[\"updated\"] = patched[\"created\"];\n fixed.push(`updated → ${String(patched[\"created\"])}`);\n }\n\n if (fixed.length === 0) return null;\n\n const parsed = matter(content);\n const newContent = matter.stringify(parsed.content, patched);\n fs.writeFileSync(factsPath, newContent);\n return { fixed, content: newContent };\n}\n\nexport async function runValidate(opts: { fix?: boolean }, dataDir: string): Promise<void> {\n const customersDir = path.join(dataDir, \"customers\");\n\n if (!fs.existsSync(customersDir)) {\n console.log(warning(\"⚠ No customers directory found.\"));\n return;\n }\n\n const slugs = listCustomerSlugs(dataDir);\n\n let errorCount = 0;\n let fixedCount = 0;\n\n for (const slug of slugs) {\n const factsPath = path.join(customersDir, slug, \"main_facts.md\");\n const interactionsPath = path.join(customersDir, slug, \"interactions.md\");\n\n if (!fs.existsSync(factsPath)) {\n console.log(error(`✗ ${slug}: missing main_facts.md`));\n errorCount++;\n continue;\n }\n\n try {\n let content = fs.readFileSync(factsPath, \"utf-8\") as string;\n const { data } = matter(content);\n\n if (opts.fix) {\n const result = applyFix(factsPath, content, data as Record<string, unknown>);\n if (result) {\n content = result.content;\n fixedCount++;\n console.log(info(`⚙ ${slug}: fixed ${result.fixed.join(\", \")}`));\n }\n }\n\n const { data: refetchedData } = matter(content);\n MainFactsSchema.parse(refetchedData);\n\n if (!fs.existsSync(interactionsPath)) {\n console.log(warning(`⚠ ${slug}: missing interactions.md`));\n } else {\n console.log(success(`✓ ${slug}`));\n }\n } catch (err) {\n console.log(error(`✗ ${slug}: ${(err as Error).message}`));\n errorCount++;\n }\n }\n\n if (opts.fix && fixedCount > 0) {\n console.log(info(`\\n⚙ Fixed ${fixedCount} customer(s).`));\n }\n\n if (errorCount > 0) {\n console.error(error(`\\n${errorCount} error(s) found.`));\n process.exit(1);\n } else {\n console.log(success(\"\\n✓ All customers valid.\"));\n }\n}\n\nexport const validateCommand = new Command(\"validate\")\n .option(\"--fix\", \"Auto-fix recoverable issues\")\n .action(async (opts: { fix?: boolean }) => {\n await runValidate(opts, process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd());\n });\n","export const VERSION = \"0.1.0\";\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/commands/create.ts","../src/commands/audit.ts","../src/commands/validate.ts","../src/version.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport path from \"path\";\nimport fs from \"fs\";\nimport slugify from \"slug\";\nimport { ensureCustomerDir, writeMainFacts } from \"../fs/customer-dir.js\";\nimport { writeFileAtomic } from \"../fs/atomic-write.js\";\nimport { writeJsonFile } from \"../fs/json-store.js\";\nimport { success, error, bold } from \"../ui/colors.js\";\n\nexport async function createCustomer(opts: {\n name: string;\n domain?: string;\n email?: string;\n dataDir?: string;\n}): Promise<{ id: string; dir: string }> {\n const id = slugify(opts.name, { lower: true });\n const dataDir = opts.dataDir ?? process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n await ensureCustomerDir(dataDir, id);\n const dir = path.join(dataDir, \"customers\", id);\n\n // Write main_facts.md\n const today = new Date().toISOString().slice(0, 10);\n await writeMainFacts(dataDir, id, {\n name: opts.name,\n domain: opts.domain,\n email: opts.email,\n relationship_stage: \"prospect\",\n tags: [],\n currency: \"EUR\",\n created: today,\n updated: today,\n });\n\n // Create interactions.md\n const interactionsPath = path.join(dir, \"interactions.md\");\n if (!fs.existsSync(interactionsPath)) {\n writeFileAtomic(interactionsPath, `# Interactions — ${opts.name}\\n\\n`);\n }\n\n // Create pipeline.md\n const pipelinePath = path.join(dir, \"pipeline.md\");\n if (!fs.existsSync(pipelinePath)) {\n writeFileAtomic(\n pipelinePath,\n `# Pipeline — ${opts.name}\\n\\n| Deal | Stage | Value | Currency | Probability | Close Date | Updated | Notes |\\n|---|---|---|---|---|---|---|---|\\n`\n );\n }\n\n // Create sources.json\n const sourcesPath = path.join(dir, \"sources.json\");\n if (!fs.existsSync(sourcesPath)) {\n const gmailQuery = opts.domain\n ? `from:${opts.domain} OR to:${opts.domain}`\n : opts.email\n ? `from:${opts.email} OR to:${opts.email}`\n : \"\";\n const sources = {\n gmail: {\n type: \"gmail\",\n query: gmailQuery,\n enabled: true,\n },\n version: 1,\n created: new Date().toISOString(),\n };\n writeJsonFile(sourcesPath, sources);\n }\n\n return { id, dir };\n}\n\nexport const createCommand = new Command(\"create\")\n .description(\"Create a new customer\")\n .argument(\"<name>\", \"Customer name\")\n .option(\"--domain <domain>\", \"Primary domain (for Gmail sync)\")\n .option(\"--email <email>\", \"Primary contact email\")\n .action(async (name: string, opts: { domain?: string; email?: string }) => {\n try {\n const { id, dir } = await createCustomer({ name, ...opts });\n console.log(success(`✓ Created customer: ${bold(id)}`));\n console.log(` Dir: ${dir}`);\n console.log(` Files: main_facts.md, interactions.md, pipeline.md, sources.json`);\n } catch (err) {\n console.error(error(`✗ ${(err as Error).message}`));\n process.exit(1);\n }\n });\n","import { Command } from \"commander\";\nimport { readAuditLog, filterAuditLog } from \"../fs/audit-log.js\";\n\nconst SEP = \"─\".repeat(70);\n\nexport async function runAudit(\n opts: {\n slug?: string;\n actor?: string;\n limit?: number;\n tail?: boolean;\n },\n dataDir?: string\n): Promise<void> {\n const dir = dataDir ?? process.cwd();\n const limit = opts.limit ?? 20;\n\n const allEntries = readAuditLog(dir);\n const entries = filterAuditLog(allEntries, {\n ...(opts.slug !== undefined ? { slug: opts.slug } : {}),\n ...(opts.actor !== undefined ? { actor: opts.actor } : {}),\n limit,\n });\n\n console.log(SEP);\n console.log(\" DatasynxOpenCRM — Audit Trail\");\n\n if (opts.slug) console.log(` Customer: ${opts.slug}`);\n if (opts.actor) console.log(` Actor: ${opts.actor}`);\n\n console.log(SEP);\n\n if (entries.length === 0) {\n console.log(\" No audit entries found.\");\n console.log(SEP);\n return;\n }\n\n for (const entry of entries) {\n console.log(\n ` ${entry.timestamp} ${entry.actor.padEnd(12)} ${entry.tool.padEnd(20)} ${entry.slug.padEnd(20)} ${entry.summary}`\n );\n }\n\n console.log(SEP);\n console.log(` ${entries.length} entr${entries.length === 1 ? \"y\" : \"ies\"} shown`);\n console.log(SEP);\n}\n\nexport const auditCommand = new Command(\"audit\")\n .description(\"Show CRM audit trail — who changed what and when\")\n .option(\"--slug <slug>\", \"Filter by customer slug\")\n .option(\"--actor <actor>\", \"Filter by actor\")\n .option(\"--limit <n>\", \"Number of entries to show (default: 20)\", parseInt)\n .option(\"--tail\", \"Show all new entries (simplified: shows current entries)\")\n .action((opts: { slug?: string; actor?: string; limit?: number; tail?: boolean }) =>\n runAudit(opts, process.env[\"DXCRM_DATA_DIR\"])\n );\n","import { Command } from \"commander\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { MainFactsSchema } from \"../schemas/main-facts.js\";\nimport { listCustomerSlugs } from \"../fs/customer-dir.js\";\nimport matter from \"gray-matter\";\nimport { success, error, warning, info } from \"../ui/colors.js\";\n\nconst RECOVERABLE_DEFAULTS: Record<string, unknown> = {\n tags: [],\n currency: \"EUR\",\n};\n\nexport function applyFix(\n factsPath: string,\n content: string,\n data: Record<string, unknown>\n): { fixed: string[]; content: string } | null {\n const fixed: string[] = [];\n const patched = { ...data };\n\n for (const [field, defaultValue] of Object.entries(RECOVERABLE_DEFAULTS)) {\n if (patched[field] === undefined || patched[field] === null) {\n patched[field] = defaultValue;\n fixed.push(`${field} → ${JSON.stringify(defaultValue)}`);\n }\n }\n\n if (patched[\"updated\"] === undefined && patched[\"created\"]) {\n patched[\"updated\"] = patched[\"created\"];\n fixed.push(`updated → ${String(patched[\"created\"])}`);\n }\n\n if (fixed.length === 0) return null;\n\n const parsed = matter(content);\n const newContent = matter.stringify(parsed.content, patched);\n fs.writeFileSync(factsPath, newContent);\n return { fixed, content: newContent };\n}\n\nexport async function runValidate(opts: { fix?: boolean }, dataDir: string): Promise<void> {\n const customersDir = path.join(dataDir, \"customers\");\n\n if (!fs.existsSync(customersDir)) {\n console.log(warning(\"⚠ No customers directory found.\"));\n return;\n }\n\n const slugs = listCustomerSlugs(dataDir);\n\n let errorCount = 0;\n let fixedCount = 0;\n\n for (const slug of slugs) {\n const factsPath = path.join(customersDir, slug, \"main_facts.md\");\n const interactionsPath = path.join(customersDir, slug, \"interactions.md\");\n\n if (!fs.existsSync(factsPath)) {\n console.log(error(`✗ ${slug}: missing main_facts.md`));\n errorCount++;\n continue;\n }\n\n try {\n let content = fs.readFileSync(factsPath, \"utf-8\") as string;\n let parsed = matter(content);\n\n if (opts.fix) {\n const result = applyFix(factsPath, content, parsed.data as Record<string, unknown>);\n if (result) {\n content = result.content;\n parsed = matter(content); // only re-parse when a fix actually rewrote content\n fixedCount++;\n console.log(info(`⚙ ${slug}: fixed ${result.fixed.join(\", \")}`));\n }\n }\n\n MainFactsSchema.parse(parsed.data);\n\n if (!fs.existsSync(interactionsPath)) {\n console.log(warning(`⚠ ${slug}: missing interactions.md`));\n } else {\n console.log(success(`✓ ${slug}`));\n }\n } catch (err) {\n console.log(error(`✗ ${slug}: ${(err as Error).message}`));\n errorCount++;\n }\n }\n\n if (opts.fix && fixedCount > 0) {\n console.log(info(`\\n⚙ Fixed ${fixedCount} customer(s).`));\n }\n\n if (errorCount > 0) {\n console.error(error(`\\n${errorCount} error(s) found.`));\n process.exit(1);\n } else {\n console.log(success(\"\\n✓ All customers valid.\"));\n }\n}\n\nexport const validateCommand = new Command(\"validate\")\n .description(\"Validate all customer data against schemas\")\n .option(\"--fix\", \"Auto-fix recoverable issues\")\n .action(async (opts: { fix?: boolean }) => {\n await runValidate(opts, process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd());\n });\n","export const VERSION = \"0.1.0\";\n"],"mappings":";;;;;;;;AASA,eAAsB,eAAe,MAKI;CACvC,MAAM,KAAK,QAAQ,KAAK,MAAM,EAAE,OAAO,KAAK,CAAC;CAC7C,MAAM,UAAU,KAAK,WAAW,QAAQ,IAAI,qBAAqB,QAAQ,IAAI;CAC7E,MAAM,kBAAkB,SAAS,EAAE;CACnC,MAAM,MAAM,KAAK,KAAK,SAAS,aAAa,EAAE;CAG9C,MAAM,yBAAQ,IAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE;CAClD,MAAM,eAAe,SAAS,IAAI;EAChC,MAAM,KAAK;EACX,QAAQ,KAAK;EACb,OAAO,KAAK;EACZ,oBAAoB;EACpB,MAAM,CAAC;EACP,UAAU;EACV,SAAS;EACT,SAAS;CACX,CAAC;CAGD,MAAM,mBAAmB,KAAK,KAAK,KAAK,iBAAiB;CACzD,IAAI,CAAC,GAAG,WAAW,gBAAgB,GACjC,gBAAgB,kBAAkB,oBAAoB,KAAK,KAAK,KAAK;CAIvE,MAAM,eAAe,KAAK,KAAK,KAAK,aAAa;CACjD,IAAI,CAAC,GAAG,WAAW,YAAY,GAC7B,gBACE,cACA,gBAAgB,KAAK,KAAK,0HAC5B;CAIF,MAAM,cAAc,KAAK,KAAK,KAAK,cAAc;CACjD,IAAI,CAAC,GAAG,WAAW,WAAW,GAe5B,cAAc,aAAa;EARzB,OAAO;GACL,MAAM;GACN,OARe,KAAK,SACpB,QAAQ,KAAK,OAAO,SAAS,KAAK,WAClC,KAAK,QACH,QAAQ,KAAK,MAAM,SAAS,KAAK,UACjC;GAKF,SAAS;EACX;EACA,SAAS;EACT,0BAAS,IAAI,KAAK,GAAE,YAAY;CAED,CAAC;CAGpC,OAAO;EAAE;EAAI;CAAI;AACnB;AAE6B,IAAI,QAAQ,QAAQ,EAC9C,YAAY,uBAAuB,EACnC,SAAS,UAAU,eAAe,EAClC,OAAO,qBAAqB,iCAAiC,EAC7D,OAAO,mBAAmB,uBAAuB,EACjD,OAAO,OAAO,MAAc,SAA8C;CACzE,IAAI;EACF,MAAM,EAAE,IAAI,QAAQ,MAAM,eAAe;GAAE;GAAM,GAAG;EAAK,CAAC;EAC1D,QAAQ,IAAI,QAAQ,uBAAuB,KAAK,EAAE,GAAG,CAAC;EACtD,QAAQ,IAAI,UAAU,KAAK;EAC3B,QAAQ,IAAI,oEAAoE;CAClF,SAAS,KAAK;EACZ,QAAQ,MAAM,MAAM,KAAM,IAAc,SAAS,CAAC;EAClD,QAAQ,KAAK,CAAC;CAChB;AACF,CAAC;;;ACnFH,MAAM,MAAM,IAAI,OAAO,EAAE;AAEzB,eAAsB,SACpB,MAMA,SACe;CACf,MAAM,MAAM,WAAW,QAAQ,IAAI;CACnC,MAAM,QAAQ,KAAK,SAAS;CAG5B,MAAM,UAAU,eADG,aAAa,GACQ,GAAG;EACzC,GAAI,KAAK,SAAS,KAAA,IAAY,EAAE,MAAM,KAAK,KAAK,IAAI,CAAC;EACrD,GAAI,KAAK,UAAU,KAAA,IAAY,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;EACxD;CACF,CAAC;CAED,QAAQ,IAAI,GAAG;CACf,QAAQ,IAAI,gCAAgC;CAE5C,IAAI,KAAK,MAAM,QAAQ,IAAI,cAAc,KAAK,MAAM;CACpD,IAAI,KAAK,OAAO,QAAQ,IAAI,cAAc,KAAK,OAAO;CAEtD,QAAQ,IAAI,GAAG;CAEf,IAAI,QAAQ,WAAW,GAAG;EACxB,QAAQ,IAAI,0BAA0B;EACtC,QAAQ,IAAI,GAAG;EACf;CACF;CAEA,KAAK,MAAM,SAAS,SAClB,QAAQ,IACN,IAAI,MAAM,UAAU,IAAI,MAAM,MAAM,OAAO,EAAE,EAAE,IAAI,MAAM,KAAK,OAAO,EAAE,EAAE,IAAI,MAAM,KAAK,OAAO,EAAE,EAAE,IAAI,MAAM,SAC/G;CAGF,QAAQ,IAAI,GAAG;CACf,QAAQ,IAAI,IAAI,QAAQ,OAAO,OAAO,QAAQ,WAAW,IAAI,MAAM,MAAM,OAAO;CAChF,QAAQ,IAAI,GAAG;AACjB;AAE4B,IAAI,QAAQ,OAAO,EAC5C,YAAY,kDAAkD,EAC9D,OAAO,iBAAiB,yBAAyB,EACjD,OAAO,mBAAmB,iBAAiB,EAC3C,OAAO,eAAe,2CAA2C,QAAQ,EACzE,OAAO,UAAU,0DAA0D,EAC3E,QAAQ,SACP,SAAS,MAAM,QAAQ,IAAI,iBAAiB,CAC9C;;;ACjDF,MAAM,uBAAgD;CACpD,MAAM,CAAC;CACP,UAAU;AACZ;AAEA,SAAgB,SACd,WACA,SACA,MAC6C;CAC7C,MAAM,QAAkB,CAAC;CACzB,MAAM,UAAU,EAAE,GAAG,KAAK;CAE1B,KAAK,MAAM,CAAC,OAAO,iBAAiB,OAAO,QAAQ,oBAAoB,GACrE,IAAI,QAAQ,WAAW,KAAA,KAAa,QAAQ,WAAW,MAAM;EAC3D,QAAQ,SAAS;EACjB,MAAM,KAAK,GAAG,MAAM,KAAK,KAAK,UAAU,YAAY,GAAG;CACzD;CAGF,IAAI,QAAQ,eAAe,KAAA,KAAa,QAAQ,YAAY;EAC1D,QAAQ,aAAa,QAAQ;EAC7B,MAAM,KAAK,aAAa,OAAO,QAAQ,UAAU,GAAG;CACtD;CAEA,IAAI,MAAM,WAAW,GAAG,OAAO;CAE/B,MAAM,SAAS,OAAO,OAAO;CAC7B,MAAM,aAAa,OAAO,UAAU,OAAO,SAAS,OAAO;CAC3D,GAAG,cAAc,WAAW,UAAU;CACtC,OAAO;EAAE;EAAO,SAAS;CAAW;AACtC;AAEA,eAAsB,YAAY,MAAyB,SAAgC;CACzF,MAAM,eAAe,KAAK,KAAK,SAAS,WAAW;CAEnD,IAAI,CAAC,GAAG,WAAW,YAAY,GAAG;EAChC,QAAQ,IAAI,QAAQ,iCAAiC,CAAC;EACtD;CACF;CAEA,MAAM,QAAQ,kBAAkB,OAAO;CAEvC,IAAI,aAAa;CACjB,IAAI,aAAa;CAEjB,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,YAAY,KAAK,KAAK,cAAc,MAAM,eAAe;EAC/D,MAAM,mBAAmB,KAAK,KAAK,cAAc,MAAM,iBAAiB;EAExE,IAAI,CAAC,GAAG,WAAW,SAAS,GAAG;GAC7B,QAAQ,IAAI,MAAM,KAAK,KAAK,wBAAwB,CAAC;GACrD;GACA;EACF;EAEA,IAAI;GACF,IAAI,UAAU,GAAG,aAAa,WAAW,OAAO;GAChD,IAAI,SAAS,OAAO,OAAO;GAE3B,IAAI,KAAK,KAAK;IACZ,MAAM,SAAS,SAAS,WAAW,SAAS,OAAO,IAA+B;IAClF,IAAI,QAAQ;KACV,UAAU,OAAO;KACjB,SAAS,OAAO,OAAO;KACvB;KACA,QAAQ,IAAI,KAAK,KAAK,KAAK,UAAU,OAAO,MAAM,KAAK,IAAI,GAAG,CAAC;IACjE;GACF;GAEA,gBAAgB,MAAM,OAAO,IAAI;GAEjC,IAAI,CAAC,GAAG,WAAW,gBAAgB,GACjC,QAAQ,IAAI,QAAQ,KAAK,KAAK,0BAA0B,CAAC;QAEzD,QAAQ,IAAI,QAAQ,KAAK,MAAM,CAAC;EAEpC,SAAS,KAAK;GACZ,QAAQ,IAAI,MAAM,KAAK,KAAK,IAAK,IAAc,SAAS,CAAC;GACzD;EACF;CACF;CAEA,IAAI,KAAK,OAAO,aAAa,GAC3B,QAAQ,IAAI,KAAK,aAAa,WAAW,cAAc,CAAC;CAG1D,IAAI,aAAa,GAAG;EAClB,QAAQ,MAAM,MAAM,KAAK,WAAW,iBAAiB,CAAC;EACtD,QAAQ,KAAK,CAAC;CAChB,OACE,QAAQ,IAAI,QAAQ,0BAA0B,CAAC;AAEnD;AAE+B,IAAI,QAAQ,UAAU,EAClD,YAAY,4CAA4C,EACxD,OAAO,SAAS,6BAA6B,EAC7C,OAAO,OAAO,SAA4B;CACzC,MAAM,YAAY,MAAM,QAAQ,IAAI,qBAAqB,QAAQ,IAAI,CAAC;AACxE,CAAC;;;AC5GH,MAAa,UAAU"}
@@ -0,0 +1,2 @@
1
+ import { i as readInteractions, n as appendInteraction, t as InteractionDedup } from "./interactions-writer-B8XAzdqR.js";
2
+ export { InteractionDedup, appendInteraction, readInteractions };
@@ -1,3 +1,5 @@
1
+ import { t as assertSafeSlug } from "./customer-dir-CkMMXhb0.js";
2
+ import { t as writeFileAtomic } from "./atomic-write-8yjqqLtS.js";
1
3
  import { t as withFileQueue } from "./write-queue-IbsAjUnh.js";
2
4
  import path from "path";
3
5
  import fs from "fs";
@@ -7,11 +9,12 @@ function formatInteractionEntry(entry) {
7
9
  const header = `## ${entry.date} · ${entry.type}${entry.direction ? ` · ${entry.direction}` : ""}`;
8
10
  const withLabel = entry.type === "Email" ? "Subject" : "With";
9
11
  const nextStepsBlock = entry.nextSteps.length > 0 ? entry.nextSteps.map((s) => `- [ ] ${s}`).join("\n") : "- [ ] —";
12
+ const attachmentsLine = entry.attachments && entry.attachments.length > 0 ? `\n**Attachments:** ${entry.attachments.map((a) => `[${a.split("/").pop()}](attachments/${a})`).join(", ")}` : "";
10
13
  return `${header}
11
14
  **${withLabel}:** ${entry.with}
12
15
  **Summary:** ${entry.summary}
13
16
  **Next Steps:**
14
- ${nextStepsBlock}
17
+ ${nextStepsBlock}${attachmentsLine}
15
18
  **Source:** ${entry.sourceRef}
16
19
  **Synced:** ${entry.synced}
17
20
  ${INTERACTION_SEPARATOR}
@@ -22,7 +25,34 @@ async function readInteractions(dataDir, slug) {
22
25
  if (!fs.existsSync(filePath)) return "";
23
26
  return fs.readFileSync(filePath, "utf-8");
24
27
  }
28
+ /**
29
+ * Per-run source-ref deduplication index for bulk imports. Loads each slug's
30
+ * interactions file once (not once per row) and tracks freshly-appended refs
31
+ * in memory, so a 5k-row import stays linear instead of re-reading a growing
32
+ * file on every row (the previous O(rows²) behavior).
33
+ */
34
+ var InteractionDedup = class {
35
+ dataDir;
36
+ cache = /* @__PURE__ */ new Map();
37
+ constructor(dataDir) {
38
+ this.dataDir = dataDir;
39
+ }
40
+ /** True if `sourceRef` already exists for `slug` (on disk or appended this run). */
41
+ async seen(slug, sourceRef) {
42
+ let content = this.cache.get(slug);
43
+ if (content === void 0) {
44
+ content = await readInteractions(this.dataDir, slug).catch(() => "");
45
+ this.cache.set(slug, content);
46
+ }
47
+ return content.includes(sourceRef);
48
+ }
49
+ /** Record that `sourceRef` was just appended, so later rows dedupe against it. */
50
+ markAppended(slug, sourceRef) {
51
+ this.cache.set(slug, (this.cache.get(slug) ?? "") + sourceRef);
52
+ }
53
+ };
25
54
  async function appendInteraction(dataDir, slug, entry) {
55
+ assertSafeSlug(slug);
26
56
  const filePath = path.join(dataDir, "customers", slug, "interactions.md");
27
57
  return withFileQueue(filePath, async () => {
28
58
  const existing = fs.existsSync(filePath) ? fs.readFileSync(filePath, "utf-8") : "";
@@ -37,10 +67,10 @@ async function appendInteraction(dataDir, slug, entry) {
37
67
  newContent = header + formatted + "\n" + body;
38
68
  } else newContent = existing + "\n" + formatted;
39
69
  }
40
- fs.writeFileSync(filePath, newContent, "utf-8");
70
+ writeFileAtomic(filePath, newContent);
41
71
  });
42
72
  }
43
73
  //#endregion
44
- export { formatInteractionEntry as n, readInteractions as r, appendInteraction as t };
74
+ export { readInteractions as i, appendInteraction as n, formatInteractionEntry as r, InteractionDedup as t };
45
75
 
46
- //# sourceMappingURL=interactions-writer-SLHnoEeE.js.map
76
+ //# sourceMappingURL=interactions-writer-B8XAzdqR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactions-writer-B8XAzdqR.js","names":[],"sources":["../src/fs/interactions-writer.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\nimport type { InteractionEntry } from \"../schemas/interaction.js\";\nimport { withFileQueue } from \"./write-queue.js\";\nimport { writeFileAtomic } from \"./atomic-write.js\";\nimport { assertSafeSlug } from \"./customer-dir.js\";\n\nconst INTERACTION_SEPARATOR = \"---\";\n\nexport function formatInteractionEntry(entry: InteractionEntry): string {\n const header = `## ${entry.date} · ${entry.type}${entry.direction ? ` · ${entry.direction}` : \"\"}`;\n const withLabel = entry.type === \"Email\" ? \"Subject\" : \"With\";\n const nextStepsBlock =\n entry.nextSteps.length > 0 ? entry.nextSteps.map((s) => `- [ ] ${s}`).join(\"\\n\") : \"- [ ] —\";\n\n const attachmentsLine =\n entry.attachments && entry.attachments.length > 0\n ? `\\n**Attachments:** ${entry.attachments\n .map((a) => `[${a.split(\"/\").pop()}](attachments/${a})`)\n .join(\", \")}`\n : \"\";\n\n return `${header}\n**${withLabel}:** ${entry.with}\n**Summary:** ${entry.summary}\n**Next Steps:**\n${nextStepsBlock}${attachmentsLine}\n**Source:** ${entry.sourceRef}\n**Synced:** ${entry.synced}\n${INTERACTION_SEPARATOR}\n`;\n}\n\nexport async function readInteractions(dataDir: string, slug: string): Promise<string> {\n const filePath = path.join(dataDir, \"customers\", slug, \"interactions.md\");\n if (!fs.existsSync(filePath)) {\n return \"\";\n }\n return fs.readFileSync(filePath, \"utf-8\");\n}\n\n/**\n * Per-run source-ref deduplication index for bulk imports. Loads each slug's\n * interactions file once (not once per row) and tracks freshly-appended refs\n * in memory, so a 5k-row import stays linear instead of re-reading a growing\n * file on every row (the previous O(rows²) behavior).\n */\nexport class InteractionDedup {\n private readonly cache = new Map<string, string>();\n constructor(private readonly dataDir: string) {}\n\n /** True if `sourceRef` already exists for `slug` (on disk or appended this run). */\n async seen(slug: string, sourceRef: string): Promise<boolean> {\n let content = this.cache.get(slug);\n if (content === undefined) {\n content = await readInteractions(this.dataDir, slug).catch(() => \"\");\n this.cache.set(slug, content);\n }\n return content.includes(sourceRef);\n }\n\n /** Record that `sourceRef` was just appended, so later rows dedupe against it. */\n markAppended(slug: string, sourceRef: string): void {\n this.cache.set(slug, (this.cache.get(slug) ?? \"\") + sourceRef);\n }\n}\n\nexport async function appendInteraction(\n dataDir: string,\n slug: string,\n entry: InteractionEntry\n): Promise<void> {\n assertSafeSlug(slug);\n const filePath = path.join(dataDir, \"customers\", slug, \"interactions.md\");\n return withFileQueue(filePath, async () => {\n const existing = fs.existsSync(filePath) ? (fs.readFileSync(filePath, \"utf-8\") as string) : \"\";\n\n const formatted = formatInteractionEntry(entry);\n\n let newContent: string;\n if (existing === \"\") {\n newContent = formatted;\n } else {\n const headerEnd = existing.indexOf(\"\\n\\n\");\n if (headerEnd > -1) {\n const header = existing.slice(0, headerEnd + 2);\n const body = existing.slice(headerEnd + 2);\n newContent = header + formatted + \"\\n\" + body;\n } else {\n newContent = existing + \"\\n\" + formatted;\n }\n }\n\n writeFileAtomic(filePath, newContent);\n });\n}\n"],"mappings":";;;;;;AAOA,MAAM,wBAAwB;AAE9B,SAAgB,uBAAuB,OAAiC;CACtE,MAAM,SAAS,MAAM,MAAM,KAAK,KAAK,MAAM,OAAO,MAAM,YAAY,MAAM,MAAM,cAAc;CAC9F,MAAM,YAAY,MAAM,SAAS,UAAU,YAAY;CACvD,MAAM,iBACJ,MAAM,UAAU,SAAS,IAAI,MAAM,UAAU,KAAK,MAAM,SAAS,GAAG,EAAE,KAAK,IAAI,IAAI;CAErF,MAAM,kBACJ,MAAM,eAAe,MAAM,YAAY,SAAS,IAC5C,sBAAsB,MAAM,YACzB,KAAK,MAAM,IAAI,EAAE,MAAM,GAAG,EAAE,IAAI,EAAE,gBAAgB,EAAE,EAAE,EACtD,KAAK,IAAI,MACZ;CAEN,OAAO,GAAG,OAAO;IACf,UAAU,MAAM,MAAM,KAAK;eAChB,MAAM,QAAQ;;EAE3B,iBAAiB,gBAAgB;cACrB,MAAM,UAAU;cAChB,MAAM,OAAO;EACzB,sBAAsB;;AAExB;AAEA,eAAsB,iBAAiB,SAAiB,MAA+B;CACrF,MAAM,WAAW,KAAK,KAAK,SAAS,aAAa,MAAM,iBAAiB;CACxE,IAAI,CAAC,GAAG,WAAW,QAAQ,GACzB,OAAO;CAET,OAAO,GAAG,aAAa,UAAU,OAAO;AAC1C;;;;;;;AAQA,IAAa,mBAAb,MAA8B;CAEC;CAD7B,wBAAyB,IAAI,IAAoB;CACjD,YAAY,SAAkC;EAAjB,KAAA,UAAA;CAAkB;;CAG/C,MAAM,KAAK,MAAc,WAAqC;EAC5D,IAAI,UAAU,KAAK,MAAM,IAAI,IAAI;EACjC,IAAI,YAAY,KAAA,GAAW;GACzB,UAAU,MAAM,iBAAiB,KAAK,SAAS,IAAI,EAAE,YAAY,EAAE;GACnE,KAAK,MAAM,IAAI,MAAM,OAAO;EAC9B;EACA,OAAO,QAAQ,SAAS,SAAS;CACnC;;CAGA,aAAa,MAAc,WAAyB;EAClD,KAAK,MAAM,IAAI,OAAO,KAAK,MAAM,IAAI,IAAI,KAAK,MAAM,SAAS;CAC/D;AACF;AAEA,eAAsB,kBACpB,SACA,MACA,OACe;CACf,eAAe,IAAI;CACnB,MAAM,WAAW,KAAK,KAAK,SAAS,aAAa,MAAM,iBAAiB;CACxE,OAAO,cAAc,UAAU,YAAY;EACzC,MAAM,WAAW,GAAG,WAAW,QAAQ,IAAK,GAAG,aAAa,UAAU,OAAO,IAAe;EAE5F,MAAM,YAAY,uBAAuB,KAAK;EAE9C,IAAI;EACJ,IAAI,aAAa,IACf,aAAa;OACR;GACL,MAAM,YAAY,SAAS,QAAQ,MAAM;GACzC,IAAI,YAAY,IAAI;IAClB,MAAM,SAAS,SAAS,MAAM,GAAG,YAAY,CAAC;IAC9C,MAAM,OAAO,SAAS,MAAM,YAAY,CAAC;IACzC,aAAa,SAAS,YAAY,OAAO;GAC3C,OACE,aAAa,WAAW,OAAO;EAEnC;EAEA,gBAAgB,UAAU,UAAU;CACtC,CAAC;AACH"}
@@ -1,4 +1,6 @@
1
1
  const require_chunk = require("./chunk-DakpK96I.cjs");
2
+ const require_session_store = require("./session-store-yfwnj0OC.cjs");
3
+ const require_atomic_write = require("./atomic-write-BYmF-ThH.cjs");
2
4
  const require_write_queue = require("./write-queue-BDolUxfs.cjs");
3
5
  let path = require("path");
4
6
  path = require_chunk.__toESM(path, 1);
@@ -15,11 +17,12 @@ function formatInteractionEntry(entry) {
15
17
  const header = `## ${entry.date} · ${entry.type}${entry.direction ? ` · ${entry.direction}` : ""}`;
16
18
  const withLabel = entry.type === "Email" ? "Subject" : "With";
17
19
  const nextStepsBlock = entry.nextSteps.length > 0 ? entry.nextSteps.map((s) => `- [ ] ${s}`).join("\n") : "- [ ] —";
20
+ const attachmentsLine = entry.attachments && entry.attachments.length > 0 ? `\n**Attachments:** ${entry.attachments.map((a) => `[${a.split("/").pop()}](attachments/${a})`).join(", ")}` : "";
18
21
  return `${header}
19
22
  **${withLabel}:** ${entry.with}
20
23
  **Summary:** ${entry.summary}
21
24
  **Next Steps:**
22
- ${nextStepsBlock}
25
+ ${nextStepsBlock}${attachmentsLine}
23
26
  **Source:** ${entry.sourceRef}
24
27
  **Synced:** ${entry.synced}
25
28
  ${INTERACTION_SEPARATOR}
@@ -31,6 +34,7 @@ async function readInteractions(dataDir, slug) {
31
34
  return fs.default.readFileSync(filePath, "utf-8");
32
35
  }
33
36
  async function appendInteraction(dataDir, slug, entry) {
37
+ require_session_store.assertSafeSlug(slug);
34
38
  const filePath = path.default.join(dataDir, "customers", slug, "interactions.md");
35
39
  return require_write_queue.withFileQueue(filePath, async () => {
36
40
  const existing = fs.default.existsSync(filePath) ? fs.default.readFileSync(filePath, "utf-8") : "";
@@ -45,7 +49,7 @@ async function appendInteraction(dataDir, slug, entry) {
45
49
  newContent = header + formatted + "\n" + body;
46
50
  } else newContent = existing + "\n" + formatted;
47
51
  }
48
- fs.default.writeFileSync(filePath, newContent, "utf-8");
52
+ require_atomic_write.writeFileAtomic(filePath, newContent);
49
53
  });
50
54
  }
51
55
  //#endregion
@@ -74,4 +78,4 @@ Object.defineProperty(exports, "readInteractions", {
74
78
  }
75
79
  });
76
80
 
77
- //# sourceMappingURL=interactions-writer-CrPStUll.cjs.map
81
+ //# sourceMappingURL=interactions-writer-BRJNrefF.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactions-writer-BRJNrefF.cjs","names":["withFileQueue"],"sources":["../src/fs/interactions-writer.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\nimport type { InteractionEntry } from \"../schemas/interaction.js\";\nimport { withFileQueue } from \"./write-queue.js\";\nimport { writeFileAtomic } from \"./atomic-write.js\";\nimport { assertSafeSlug } from \"./customer-dir.js\";\n\nconst INTERACTION_SEPARATOR = \"---\";\n\nexport function formatInteractionEntry(entry: InteractionEntry): string {\n const header = `## ${entry.date} · ${entry.type}${entry.direction ? ` · ${entry.direction}` : \"\"}`;\n const withLabel = entry.type === \"Email\" ? \"Subject\" : \"With\";\n const nextStepsBlock =\n entry.nextSteps.length > 0 ? entry.nextSteps.map((s) => `- [ ] ${s}`).join(\"\\n\") : \"- [ ] —\";\n\n const attachmentsLine =\n entry.attachments && entry.attachments.length > 0\n ? `\\n**Attachments:** ${entry.attachments\n .map((a) => `[${a.split(\"/\").pop()}](attachments/${a})`)\n .join(\", \")}`\n : \"\";\n\n return `${header}\n**${withLabel}:** ${entry.with}\n**Summary:** ${entry.summary}\n**Next Steps:**\n${nextStepsBlock}${attachmentsLine}\n**Source:** ${entry.sourceRef}\n**Synced:** ${entry.synced}\n${INTERACTION_SEPARATOR}\n`;\n}\n\nexport async function readInteractions(dataDir: string, slug: string): Promise<string> {\n const filePath = path.join(dataDir, \"customers\", slug, \"interactions.md\");\n if (!fs.existsSync(filePath)) {\n return \"\";\n }\n return fs.readFileSync(filePath, \"utf-8\");\n}\n\n/**\n * Per-run source-ref deduplication index for bulk imports. Loads each slug's\n * interactions file once (not once per row) and tracks freshly-appended refs\n * in memory, so a 5k-row import stays linear instead of re-reading a growing\n * file on every row (the previous O(rows²) behavior).\n */\nexport class InteractionDedup {\n private readonly cache = new Map<string, string>();\n constructor(private readonly dataDir: string) {}\n\n /** True if `sourceRef` already exists for `slug` (on disk or appended this run). */\n async seen(slug: string, sourceRef: string): Promise<boolean> {\n let content = this.cache.get(slug);\n if (content === undefined) {\n content = await readInteractions(this.dataDir, slug).catch(() => \"\");\n this.cache.set(slug, content);\n }\n return content.includes(sourceRef);\n }\n\n /** Record that `sourceRef` was just appended, so later rows dedupe against it. */\n markAppended(slug: string, sourceRef: string): void {\n this.cache.set(slug, (this.cache.get(slug) ?? \"\") + sourceRef);\n }\n}\n\nexport async function appendInteraction(\n dataDir: string,\n slug: string,\n entry: InteractionEntry\n): Promise<void> {\n assertSafeSlug(slug);\n const filePath = path.join(dataDir, \"customers\", slug, \"interactions.md\");\n return withFileQueue(filePath, async () => {\n const existing = fs.existsSync(filePath) ? (fs.readFileSync(filePath, \"utf-8\") as string) : \"\";\n\n const formatted = formatInteractionEntry(entry);\n\n let newContent: string;\n if (existing === \"\") {\n newContent = formatted;\n } else {\n const headerEnd = existing.indexOf(\"\\n\\n\");\n if (headerEnd > -1) {\n const header = existing.slice(0, headerEnd + 2);\n const body = existing.slice(headerEnd + 2);\n newContent = header + formatted + \"\\n\" + body;\n } else {\n newContent = existing + \"\\n\" + formatted;\n }\n }\n\n writeFileAtomic(filePath, newContent);\n });\n}\n"],"mappings":";;;;;;;;;;;;;;AAOA,MAAM,wBAAwB;AAE9B,SAAgB,uBAAuB,OAAiC;CACtE,MAAM,SAAS,MAAM,MAAM,KAAK,KAAK,MAAM,OAAO,MAAM,YAAY,MAAM,MAAM,cAAc;CAC9F,MAAM,YAAY,MAAM,SAAS,UAAU,YAAY;CACvD,MAAM,iBACJ,MAAM,UAAU,SAAS,IAAI,MAAM,UAAU,KAAK,MAAM,SAAS,GAAG,EAAE,KAAK,IAAI,IAAI;CAErF,MAAM,kBACJ,MAAM,eAAe,MAAM,YAAY,SAAS,IAC5C,sBAAsB,MAAM,YACzB,KAAK,MAAM,IAAI,EAAE,MAAM,GAAG,EAAE,IAAI,EAAE,gBAAgB,EAAE,EAAE,EACtD,KAAK,IAAI,MACZ;CAEN,OAAO,GAAG,OAAO;IACf,UAAU,MAAM,MAAM,KAAK;eAChB,MAAM,QAAQ;;EAE3B,iBAAiB,gBAAgB;cACrB,MAAM,UAAU;cAChB,MAAM,OAAO;EACzB,sBAAsB;;AAExB;AAEA,eAAsB,iBAAiB,SAAiB,MAA+B;CACrF,MAAM,WAAW,KAAA,QAAK,KAAK,SAAS,aAAa,MAAM,iBAAiB;CACxE,IAAI,CAAC,GAAA,QAAG,WAAW,QAAQ,GACzB,OAAO;CAET,OAAO,GAAA,QAAG,aAAa,UAAU,OAAO;AAC1C;AA4BA,eAAsB,kBACpB,SACA,MACA,OACe;CACf,sBAAA,eAAe,IAAI;CACnB,MAAM,WAAW,KAAA,QAAK,KAAK,SAAS,aAAa,MAAM,iBAAiB;CACxE,OAAOA,oBAAAA,cAAc,UAAU,YAAY;EACzC,MAAM,WAAW,GAAA,QAAG,WAAW,QAAQ,IAAK,GAAA,QAAG,aAAa,UAAU,OAAO,IAAe;EAE5F,MAAM,YAAY,uBAAuB,KAAK;EAE9C,IAAI;EACJ,IAAI,aAAa,IACf,aAAa;OACR;GACL,MAAM,YAAY,SAAS,QAAQ,MAAM;GACzC,IAAI,YAAY,IAAI;IAClB,MAAM,SAAS,SAAS,MAAM,GAAG,YAAY,CAAC;IAC9C,MAAM,OAAO,SAAS,MAAM,YAAY,CAAC;IACzC,aAAa,SAAS,YAAY,OAAO;GAC3C,OACE,aAAa,WAAW,OAAO;EAEnC;EAEA,qBAAA,gBAAgB,UAAU,UAAU;CACtC,CAAC;AACH"}
@@ -1,4 +1,6 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
+ import { T as assertSafeSlug } from "./session-store-DWxJ5Pof.js";
3
+ import { t as writeFileAtomic } from "./atomic-write-8yjqqLtS.js";
2
4
  import { t as withFileQueue } from "./write-queue-IbsAjUnh.js";
3
5
  import path from "path";
4
6
  import fs from "fs";
@@ -13,11 +15,12 @@ function formatInteractionEntry(entry) {
13
15
  const header = `## ${entry.date} · ${entry.type}${entry.direction ? ` · ${entry.direction}` : ""}`;
14
16
  const withLabel = entry.type === "Email" ? "Subject" : "With";
15
17
  const nextStepsBlock = entry.nextSteps.length > 0 ? entry.nextSteps.map((s) => `- [ ] ${s}`).join("\n") : "- [ ] —";
18
+ const attachmentsLine = entry.attachments && entry.attachments.length > 0 ? `\n**Attachments:** ${entry.attachments.map((a) => `[${a.split("/").pop()}](attachments/${a})`).join(", ")}` : "";
16
19
  return `${header}
17
20
  **${withLabel}:** ${entry.with}
18
21
  **Summary:** ${entry.summary}
19
22
  **Next Steps:**
20
- ${nextStepsBlock}
23
+ ${nextStepsBlock}${attachmentsLine}
21
24
  **Source:** ${entry.sourceRef}
22
25
  **Synced:** ${entry.synced}
23
26
  ${INTERACTION_SEPARATOR}
@@ -29,6 +32,7 @@ async function readInteractions(dataDir, slug) {
29
32
  return fs.readFileSync(filePath, "utf-8");
30
33
  }
31
34
  async function appendInteraction(dataDir, slug, entry) {
35
+ assertSafeSlug(slug);
32
36
  const filePath = path.join(dataDir, "customers", slug, "interactions.md");
33
37
  return withFileQueue(filePath, async () => {
34
38
  const existing = fs.existsSync(filePath) ? fs.readFileSync(filePath, "utf-8") : "";
@@ -43,10 +47,10 @@ async function appendInteraction(dataDir, slug, entry) {
43
47
  newContent = header + formatted + "\n" + body;
44
48
  } else newContent = existing + "\n" + formatted;
45
49
  }
46
- fs.writeFileSync(filePath, newContent, "utf-8");
50
+ writeFileAtomic(filePath, newContent);
47
51
  });
48
52
  }
49
53
  //#endregion
50
54
  export { readInteractions as i, formatInteractionEntry as n, interactions_writer_exports as r, appendInteraction as t };
51
55
 
52
- //# sourceMappingURL=interactions-writer-DO3KcSR3.js.map
56
+ //# sourceMappingURL=interactions-writer-ZQcpFOh9.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactions-writer-ZQcpFOh9.js","names":[],"sources":["../src/fs/interactions-writer.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\nimport type { InteractionEntry } from \"../schemas/interaction.js\";\nimport { withFileQueue } from \"./write-queue.js\";\nimport { writeFileAtomic } from \"./atomic-write.js\";\nimport { assertSafeSlug } from \"./customer-dir.js\";\n\nconst INTERACTION_SEPARATOR = \"---\";\n\nexport function formatInteractionEntry(entry: InteractionEntry): string {\n const header = `## ${entry.date} · ${entry.type}${entry.direction ? ` · ${entry.direction}` : \"\"}`;\n const withLabel = entry.type === \"Email\" ? \"Subject\" : \"With\";\n const nextStepsBlock =\n entry.nextSteps.length > 0 ? entry.nextSteps.map((s) => `- [ ] ${s}`).join(\"\\n\") : \"- [ ] —\";\n\n const attachmentsLine =\n entry.attachments && entry.attachments.length > 0\n ? `\\n**Attachments:** ${entry.attachments\n .map((a) => `[${a.split(\"/\").pop()}](attachments/${a})`)\n .join(\", \")}`\n : \"\";\n\n return `${header}\n**${withLabel}:** ${entry.with}\n**Summary:** ${entry.summary}\n**Next Steps:**\n${nextStepsBlock}${attachmentsLine}\n**Source:** ${entry.sourceRef}\n**Synced:** ${entry.synced}\n${INTERACTION_SEPARATOR}\n`;\n}\n\nexport async function readInteractions(dataDir: string, slug: string): Promise<string> {\n const filePath = path.join(dataDir, \"customers\", slug, \"interactions.md\");\n if (!fs.existsSync(filePath)) {\n return \"\";\n }\n return fs.readFileSync(filePath, \"utf-8\");\n}\n\n/**\n * Per-run source-ref deduplication index for bulk imports. Loads each slug's\n * interactions file once (not once per row) and tracks freshly-appended refs\n * in memory, so a 5k-row import stays linear instead of re-reading a growing\n * file on every row (the previous O(rows²) behavior).\n */\nexport class InteractionDedup {\n private readonly cache = new Map<string, string>();\n constructor(private readonly dataDir: string) {}\n\n /** True if `sourceRef` already exists for `slug` (on disk or appended this run). */\n async seen(slug: string, sourceRef: string): Promise<boolean> {\n let content = this.cache.get(slug);\n if (content === undefined) {\n content = await readInteractions(this.dataDir, slug).catch(() => \"\");\n this.cache.set(slug, content);\n }\n return content.includes(sourceRef);\n }\n\n /** Record that `sourceRef` was just appended, so later rows dedupe against it. */\n markAppended(slug: string, sourceRef: string): void {\n this.cache.set(slug, (this.cache.get(slug) ?? \"\") + sourceRef);\n }\n}\n\nexport async function appendInteraction(\n dataDir: string,\n slug: string,\n entry: InteractionEntry\n): Promise<void> {\n assertSafeSlug(slug);\n const filePath = path.join(dataDir, \"customers\", slug, \"interactions.md\");\n return withFileQueue(filePath, async () => {\n const existing = fs.existsSync(filePath) ? (fs.readFileSync(filePath, \"utf-8\") as string) : \"\";\n\n const formatted = formatInteractionEntry(entry);\n\n let newContent: string;\n if (existing === \"\") {\n newContent = formatted;\n } else {\n const headerEnd = existing.indexOf(\"\\n\\n\");\n if (headerEnd > -1) {\n const header = existing.slice(0, headerEnd + 2);\n const body = existing.slice(headerEnd + 2);\n newContent = header + formatted + \"\\n\" + body;\n } else {\n newContent = existing + \"\\n\" + formatted;\n }\n }\n\n writeFileAtomic(filePath, newContent);\n });\n}\n"],"mappings":";;;;;;;;;;;;AAOA,MAAM,wBAAwB;AAE9B,SAAgB,uBAAuB,OAAiC;CACtE,MAAM,SAAS,MAAM,MAAM,KAAK,KAAK,MAAM,OAAO,MAAM,YAAY,MAAM,MAAM,cAAc;CAC9F,MAAM,YAAY,MAAM,SAAS,UAAU,YAAY;CACvD,MAAM,iBACJ,MAAM,UAAU,SAAS,IAAI,MAAM,UAAU,KAAK,MAAM,SAAS,GAAG,EAAE,KAAK,IAAI,IAAI;CAErF,MAAM,kBACJ,MAAM,eAAe,MAAM,YAAY,SAAS,IAC5C,sBAAsB,MAAM,YACzB,KAAK,MAAM,IAAI,EAAE,MAAM,GAAG,EAAE,IAAI,EAAE,gBAAgB,EAAE,EAAE,EACtD,KAAK,IAAI,MACZ;CAEN,OAAO,GAAG,OAAO;IACf,UAAU,MAAM,MAAM,KAAK;eAChB,MAAM,QAAQ;;EAE3B,iBAAiB,gBAAgB;cACrB,MAAM,UAAU;cAChB,MAAM,OAAO;EACzB,sBAAsB;;AAExB;AAEA,eAAsB,iBAAiB,SAAiB,MAA+B;CACrF,MAAM,WAAW,KAAK,KAAK,SAAS,aAAa,MAAM,iBAAiB;CACxE,IAAI,CAAC,GAAG,WAAW,QAAQ,GACzB,OAAO;CAET,OAAO,GAAG,aAAa,UAAU,OAAO;AAC1C;AA4BA,eAAsB,kBACpB,SACA,MACA,OACe;CACf,eAAe,IAAI;CACnB,MAAM,WAAW,KAAK,KAAK,SAAS,aAAa,MAAM,iBAAiB;CACxE,OAAO,cAAc,UAAU,YAAY;EACzC,MAAM,WAAW,GAAG,WAAW,QAAQ,IAAK,GAAG,aAAa,UAAU,OAAO,IAAe;EAE5F,MAAM,YAAY,uBAAuB,KAAK;EAE9C,IAAI;EACJ,IAAI,aAAa,IACf,aAAa;OACR;GACL,MAAM,YAAY,SAAS,QAAQ,MAAM;GACzC,IAAI,YAAY,IAAI;IAClB,MAAM,SAAS,SAAS,MAAM,GAAG,YAAY,CAAC;IAC9C,MAAM,OAAO,SAAS,MAAM,YAAY,CAAC;IACzC,aAAa,SAAS,YAAY,OAAO;GAC3C,OACE,aAAa,WAAW,OAAO;EAEnC;EAEA,gBAAgB,UAAU,UAAU;CACtC,CAAC;AACH"}
@@ -0,0 +1,43 @@
1
+ import { t as writeFileAtomic } from "./atomic-write-8yjqqLtS.js";
2
+ import fs from "fs";
3
+ //#region src/fs/json-store.ts
4
+ /**
5
+ * Small shared JSON persistence helpers. Many modules independently reimplemented
6
+ * the same "read a JSON file, fall back to a default on missing/parse-error" and
7
+ * "write a `{ key: items }` array store" logic — this centralizes both so the
8
+ * behavior (and the silent-fallback semantics) is defined in exactly one place.
9
+ *
10
+ * Writes go through writeFileAtomic, so a crash mid-write can never leave a
11
+ * half-written (corrupt) JSON file — readers always see either the old or the
12
+ * new complete document. This matters for the config, audit, and state files
13
+ * the whole product depends on.
14
+ */
15
+ /** Read and parse a JSON file, returning `fallback` if it is missing or invalid. */
16
+ function readJsonFile(filePath, fallback) {
17
+ if (!fs.existsSync(filePath)) return fallback;
18
+ try {
19
+ return JSON.parse(fs.readFileSync(filePath, "utf-8"));
20
+ } catch {
21
+ return fallback;
22
+ }
23
+ }
24
+ /** Atomically write `value` as pretty-printed JSON, creating parent dirs as needed. */
25
+ function writeJsonFile(filePath, value) {
26
+ writeFileAtomic(filePath, JSON.stringify(value, null, 2));
27
+ }
28
+ /**
29
+ * Read an array stored under `key` in a `{ [key]: T[] }` JSON document. Returns
30
+ * an empty array if the file is missing, unparsable, or the key is not an array.
31
+ */
32
+ function readJsonArray(filePath, key) {
33
+ const arr = readJsonFile(filePath, {})[key];
34
+ return Array.isArray(arr) ? arr : [];
35
+ }
36
+ /** Write an array under `key` as a `{ [key]: items }` JSON document. */
37
+ function writeJsonArray(filePath, key, items) {
38
+ writeJsonFile(filePath, { [key]: items });
39
+ }
40
+ //#endregion
41
+ export { writeJsonFile as i, readJsonFile as n, writeJsonArray as r, readJsonArray as t };
42
+
43
+ //# sourceMappingURL=json-store-WWsFzXub.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-store-WWsFzXub.js","names":[],"sources":["../src/fs/json-store.ts"],"sourcesContent":["import fs from \"fs\";\nimport { writeFileAtomic } from \"./atomic-write.js\";\n\n/**\n * Small shared JSON persistence helpers. Many modules independently reimplemented\n * the same \"read a JSON file, fall back to a default on missing/parse-error\" and\n * \"write a `{ key: items }` array store\" logic — this centralizes both so the\n * behavior (and the silent-fallback semantics) is defined in exactly one place.\n *\n * Writes go through writeFileAtomic, so a crash mid-write can never leave a\n * half-written (corrupt) JSON file — readers always see either the old or the\n * new complete document. This matters for the config, audit, and state files\n * the whole product depends on.\n */\n\n/** Read and parse a JSON file, returning `fallback` if it is missing or invalid. */\nexport function readJsonFile<T>(filePath: string, fallback: T): T {\n if (!fs.existsSync(filePath)) return fallback;\n try {\n return JSON.parse(fs.readFileSync(filePath, \"utf-8\") as string) as T;\n } catch {\n return fallback;\n }\n}\n\n/** Atomically write `value` as pretty-printed JSON, creating parent dirs as needed. */\nexport function writeJsonFile(filePath: string, value: unknown): void {\n writeFileAtomic(filePath, JSON.stringify(value, null, 2));\n}\n\n/**\n * Read an array stored under `key` in a `{ [key]: T[] }` JSON document. Returns\n * an empty array if the file is missing, unparsable, or the key is not an array.\n */\nexport function readJsonArray<T>(filePath: string, key: string): T[] {\n const data = readJsonFile<Record<string, unknown>>(filePath, {});\n const arr = data[key];\n return Array.isArray(arr) ? (arr as T[]) : [];\n}\n\n/** Write an array under `key` as a `{ [key]: items }` JSON document. */\nexport function writeJsonArray<T>(filePath: string, key: string, items: T[]): void {\n writeJsonFile(filePath, { [key]: items });\n}\n"],"mappings":";;;;;;;;;;;;;;;AAgBA,SAAgB,aAAgB,UAAkB,UAAgB;CAChE,IAAI,CAAC,GAAG,WAAW,QAAQ,GAAG,OAAO;CACrC,IAAI;EACF,OAAO,KAAK,MAAM,GAAG,aAAa,UAAU,OAAO,CAAW;CAChE,QAAQ;EACN,OAAO;CACT;AACF;;AAGA,SAAgB,cAAc,UAAkB,OAAsB;CACpE,gBAAgB,UAAU,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAC1D;;;;;AAMA,SAAgB,cAAiB,UAAkB,KAAkB;CAEnE,MAAM,MADO,aAAsC,UAAU,CAAC,CAC/C,EAAE;CACjB,OAAO,MAAM,QAAQ,GAAG,IAAK,MAAc,CAAC;AAC9C;;AAGA,SAAgB,eAAkB,UAAkB,KAAa,OAAkB;CACjF,cAAc,UAAU,GAAG,MAAM,MAAM,CAAC;AAC1C"}
@@ -1,3 +1,5 @@
1
+ import { t as writeFileAtomic } from "./atomic-write-8yjqqLtS.js";
2
+ import { n as isSafePathSegment, t as assertSafePathSegment } from "./safe-path-mpp0dKtO.js";
1
3
  import path from "path";
2
4
  import fs from "fs";
3
5
  import matter from "gray-matter";
@@ -54,7 +56,7 @@ Config: \`.agentic/rbac.json\` | Actor: \`DXCRM_ACTOR\` env var
54
56
  | log_interaction | Write a new interaction entry (call, email, meeting, note) — immediately searchable | rep+ |
55
57
  | update_deal | Create or update a deal in pipeline.md — upserts by deal name | rep+ |
56
58
  | update_customer_facts | Update fields in customer profile (domain, contact, stage, tags) | admin |
57
- | export_customer | Export all customer data as JSON or Markdown | admin |
59
+ | export_customer | Export all customer data (incl. attachment contents) as JSON or Markdown | admin |
58
60
  | get_deal_health | Score deal health 0–100 (A–F grade) based on activity, velocity, close date, probability | any |
59
61
  | get_pipeline_forecast | Aggregate weighted pipeline revenue across all customers grouped by stage | any |
60
62
  | get_pipeline_stages | List all configured pipeline stages (defaults: lead, qualified, proposal, negotiation, won, lost) | any |
@@ -98,6 +100,7 @@ Config: \`.agentic/rbac.json\` | Actor: \`DXCRM_ACTOR\` env var
98
100
  | list_backups | List available backups with date, size, verification status, and customer count | any |
99
101
  | trigger_sync | Force immediate Gmail sync for one or all customers | rep+ |
100
102
  | get_audit_log | Read audit log — all write operations with actor, tool, customer | admin |
103
+ | get_logs | Query/aggregate the structured application log (level, component, errors) | admin |
101
104
  | define_custom_object | Define a runtime custom object type with typed fields (no migration) | admin |
102
105
  | create_record | Create a record of a custom object (validated against its schema) | rep+ |
103
106
  | list_records | List records of a custom object | any |
@@ -187,12 +190,14 @@ RBAC: admin
187
190
  - Input: slug (required) + any combination of the optional fields
188
191
  - Returns: { success: boolean, facts: object }
189
192
 
190
- ### export_customer({ slug, format? })
191
- Export all customer data (main_facts + interactions count + pipeline + attachments list).
193
+ ### export_customer({ slug, format?, includeAttachmentContent? })
194
+ Export all customer data (main_facts + interactions + pipeline + attachments).
195
+ Set includeAttachmentContent to inline every attachment's converted Markdown —
196
+ a single sendable bundle of all conversations and documents for the customer.
192
197
  RBAC: admin
193
- - Input: { slug: string, format?: "json" | "markdown" (default "json") }
194
- - Returns (JSON): { slug, exportedAt, mainFacts, interactionsCount, pipeline, attachments }
195
- - Returns (Markdown): Formatted document with all sections
198
+ - Input: { slug: string, format?: "json" | "markdown" (default "json"), includeAttachmentContent?: boolean (default false) }
199
+ - Returns (JSON): { slug, exportedAt, mainFacts, interactionsCount, pipeline, attachments[, attachmentContents] }
200
+ - Returns (Markdown): Formatted document with all sections (and attachment contents when requested)
196
201
 
197
202
  ### get_deal_health({ slug })
198
203
  Score the health of all deals for a customer based on activity recency, stage velocity,
@@ -950,18 +955,21 @@ const KbArticleSchema = z.object({
950
955
  function kbDir(dataDir) {
951
956
  return path.join(dataDir, ".agentic", "knowledge-base");
952
957
  }
953
- function listKbArticles(dataDir, opts) {
954
- const dir = kbDir(dataDir);
958
+ /** Category subdirectories of the knowledge base. */
959
+ function kbCategories(dir) {
955
960
  if (!fs.existsSync(dir)) return [];
956
- const results = [];
957
- const categories = fs.readdirSync(dir).filter((f) => {
961
+ return fs.readdirSync(dir).filter((f) => {
958
962
  try {
959
963
  return fs.statSync(path.join(dir, f)).isDirectory();
960
964
  } catch {
961
965
  return false;
962
966
  }
963
967
  });
964
- for (const cat of categories) {
968
+ }
969
+ function listKbArticles(dataDir, opts) {
970
+ const dir = kbDir(dataDir);
971
+ const results = [];
972
+ for (const cat of kbCategories(dir)) {
965
973
  const catDir = path.join(dir, cat);
966
974
  const files = fs.readdirSync(catDir).filter((f) => f.endsWith(".md"));
967
975
  for (const file of files) try {
@@ -981,22 +989,43 @@ function listKbArticles(dataDir, opts) {
981
989
  return results;
982
990
  }
983
991
  function getKbArticle(dataDir, id) {
984
- return listKbArticles(dataDir).find((a) => a.id === id) ?? null;
992
+ if (!isSafePathSegment(id)) return null;
993
+ const dir = kbDir(dataDir);
994
+ for (const cat of kbCategories(dir)) {
995
+ const filePath = path.join(dir, cat, `${id}.md`);
996
+ if (!fs.existsSync(filePath)) continue;
997
+ try {
998
+ const parsed = matter(fs.readFileSync(filePath, "utf-8"));
999
+ const meta = KbArticleSchema.safeParse(parsed.data);
1000
+ if (!meta.success) return null;
1001
+ return {
1002
+ ...meta.data,
1003
+ body: parsed.content.trim()
1004
+ };
1005
+ } catch {
1006
+ return null;
1007
+ }
1008
+ }
1009
+ return null;
985
1010
  }
986
1011
  function writeKbArticle(dataDir, article) {
987
- const dir = path.join(kbDir(dataDir), article.category);
988
- fs.mkdirSync(dir, { recursive: true });
1012
+ assertSafePathSegment(article.category, "knowledge-base category");
1013
+ assertSafePathSegment(article.id, "knowledge-base article id");
989
1014
  const { body, ...meta } = article;
990
1015
  const content = matter.stringify(body, meta);
991
- fs.writeFileSync(path.join(dir, `${article.id}.md`), content, "utf-8");
1016
+ writeFileAtomic(path.join(kbDir(dataDir), article.category, `${article.id}.md`), content);
992
1017
  }
993
1018
  function deleteKbArticle(dataDir, id) {
994
- const article = listKbArticles(dataDir).find((a) => a.id === id);
995
- if (!article) return false;
996
- const p = path.join(kbDir(dataDir), article.category, `${id}.md`);
997
- if (!fs.existsSync(p)) return false;
998
- fs.unlinkSync(p);
999
- return true;
1019
+ if (!isSafePathSegment(id)) return false;
1020
+ const dir = kbDir(dataDir);
1021
+ for (const cat of kbCategories(dir)) {
1022
+ const p = path.join(dir, cat, `${id}.md`);
1023
+ if (fs.existsSync(p)) {
1024
+ fs.unlinkSync(p);
1025
+ return true;
1026
+ }
1027
+ }
1028
+ return false;
1000
1029
  }
1001
1030
  function searchKbSimple(dataDir, query, opts) {
1002
1031
  const all = listKbArticles(dataDir, opts?.publicOnly ? { publicOnly: true } : {});
@@ -1010,4 +1039,4 @@ function getKbMetaForExport(article) {
1010
1039
  //#endregion
1011
1040
  export { searchKbSimple as a, listKbArticles as i, getKbArticle as n, writeKbArticle as o, getKbMetaForExport as r, CAPABILITIES_TEXT as s, deleteKbArticle as t };
1012
1041
 
1013
- //# sourceMappingURL=knowledge-base-D0Fh40kc.js.map
1042
+ //# sourceMappingURL=knowledge-base--063Kpa3.js.map