@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
@@ -1 +0,0 @@
1
- {"version":3,"file":"session-B9AilxOE.js","names":[],"sources":["../src/commands/session.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { setSession, getSession, clearSession } from \"../core/session-store.js\";\nimport { readMainFacts, customerExists } from \"../fs/customer-dir.js\";\nimport { success, error, info } from \"../ui/colors.js\";\n\nfunction sessionsDir(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"sessions\");\n}\n\nexport function persistSession(\n dataDir: string,\n session: {\n customerSlug: string;\n customerName: string;\n startedAt: string;\n owner?: string;\n }\n): void {\n const dir = sessionsDir(dataDir);\n fs.mkdirSync(dir, { recursive: true });\n const key = (session.owner ?? `pid-${process.pid}`).replace(/[^a-zA-Z0-9_-]/g, \"_\");\n fs.writeFileSync(path.join(dir, `${key}.json`), JSON.stringify({ ...session, pid: process.pid }));\n}\n\nexport function clearPersistedSession(dataDir: string, owner?: string): void {\n const dir = sessionsDir(dataDir);\n if (!fs.existsSync(dir)) return;\n if (owner !== undefined) {\n const key = owner.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n const file = path.join(dir, `${key}.json`);\n if (fs.existsSync(file)) fs.unlinkSync(file);\n return;\n }\n // No specific owner — clear all session files\n try {\n for (const f of fs.readdirSync(dir).filter((n) => n.endsWith(\".json\"))) {\n try {\n fs.unlinkSync(path.join(dir, f));\n } catch {\n /* ignore */\n }\n }\n } catch {\n /* ignore */\n }\n}\n\nexport function readAllSessions(\n dataDir: string\n): Array<{ customerSlug: string; customerName: string; startedAt: string; owner?: string }> {\n const dir = sessionsDir(dataDir);\n if (!fs.existsSync(dir)) return [];\n return fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(\".json\"))\n .map((f) => {\n try {\n return JSON.parse(fs.readFileSync(path.join(dir, f), \"utf-8\")) as {\n customerSlug: string;\n customerName: string;\n startedAt: string;\n owner?: string;\n };\n } catch {\n return null;\n }\n })\n .filter((s): s is NonNullable<typeof s> => s !== null);\n}\n\nexport const sessionCommand = new Command(\"session\");\n\nsessionCommand\n .command(\"open <slug>\")\n .option(\"--owner <owner>\", \"Set the owner of this session\")\n .action(async (slug: string, opts: { owner?: string }) => {\n const dataDir = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n if (!customerExists(dataDir, slug)) {\n console.error(error(`✗ Customer not found: ${slug}`));\n process.exit(1);\n }\n const facts = await readMainFacts(dataDir, slug);\n const owner = opts.owner ?? process.env[\"DXCRM_ACTOR\"];\n const session = {\n customerSlug: slug,\n customerName: facts.name,\n startedAt: new Date().toISOString(),\n ...(owner !== undefined ? { owner } : {}),\n };\n setSession(session);\n persistSession(dataDir, session);\n console.log(success(`✓ Session opened: ${facts.name}`));\n });\n\nsessionCommand.command(\"close\").action(() => {\n const dataDir = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n const s = getSession() ?? readAllSessions(dataDir)[0] ?? null;\n clearSession();\n clearPersistedSession(dataDir, s?.owner);\n console.log(success(\"✓ Session closed.\"));\n});\n\nsessionCommand.command(\"status\").action(() => {\n const dataDir = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n const s = getSession() ?? readAllSessions(dataDir)[0] ?? null;\n if (!s) {\n console.log(info(\"No active session.\"));\n } else {\n console.log(info(`Active: ${s.customerName} (${s.customerSlug}) since ${s.startedAt}`));\n }\n});\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"session-D0qFkBla.cjs","names":["Command","customerExists","error","readMainFacts","success","getSession","info"],"sources":["../src/commands/session.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { setSession, getSession, clearSession } from \"../core/session-store.js\";\nimport { readMainFacts, customerExists } from \"../fs/customer-dir.js\";\nimport { success, error, info } from \"../ui/colors.js\";\n\nfunction sessionsDir(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"sessions\");\n}\n\nexport function persistSession(\n dataDir: string,\n session: {\n customerSlug: string;\n customerName: string;\n startedAt: string;\n owner?: string;\n }\n): void {\n const dir = sessionsDir(dataDir);\n fs.mkdirSync(dir, { recursive: true });\n const key = (session.owner ?? `pid-${process.pid}`).replace(/[^a-zA-Z0-9_-]/g, \"_\");\n fs.writeFileSync(path.join(dir, `${key}.json`), JSON.stringify({ ...session, pid: process.pid }));\n}\n\nexport function clearPersistedSession(dataDir: string, owner?: string): void {\n const dir = sessionsDir(dataDir);\n if (!fs.existsSync(dir)) return;\n if (owner !== undefined) {\n const key = owner.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n const file = path.join(dir, `${key}.json`);\n if (fs.existsSync(file)) fs.unlinkSync(file);\n return;\n }\n // No specific owner — clear all session files\n try {\n for (const f of fs.readdirSync(dir).filter((n) => n.endsWith(\".json\"))) {\n try {\n fs.unlinkSync(path.join(dir, f));\n } catch {\n /* ignore */\n }\n }\n } catch {\n /* ignore */\n }\n}\n\nexport function readAllSessions(\n dataDir: string\n): Array<{ customerSlug: string; customerName: string; startedAt: string; owner?: string }> {\n const dir = sessionsDir(dataDir);\n if (!fs.existsSync(dir)) return [];\n return fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(\".json\"))\n .map((f) => {\n try {\n return JSON.parse(fs.readFileSync(path.join(dir, f), \"utf-8\")) as {\n customerSlug: string;\n customerName: string;\n startedAt: string;\n owner?: string;\n };\n } catch {\n return null;\n }\n })\n .filter((s): s is NonNullable<typeof s> => s !== null);\n}\n\nexport const sessionCommand = new Command(\"session\");\n\nsessionCommand\n .command(\"open <slug>\")\n .option(\"--owner <owner>\", \"Set the owner of this session\")\n .action(async (slug: string, opts: { owner?: string }) => {\n const dataDir = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n if (!customerExists(dataDir, slug)) {\n console.error(error(`✗ Customer not found: ${slug}`));\n process.exit(1);\n }\n const facts = await readMainFacts(dataDir, slug);\n const owner = opts.owner ?? process.env[\"DXCRM_ACTOR\"];\n const session = {\n customerSlug: slug,\n customerName: facts.name,\n startedAt: new Date().toISOString(),\n ...(owner !== undefined ? { owner } : {}),\n };\n setSession(session);\n persistSession(dataDir, session);\n console.log(success(`✓ Session opened: ${facts.name}`));\n });\n\nsessionCommand.command(\"close\").action(() => {\n const dataDir = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n const s = getSession() ?? readAllSessions(dataDir)[0] ?? null;\n clearSession();\n clearPersistedSession(dataDir, s?.owner);\n console.log(success(\"✓ Session closed.\"));\n});\n\nsessionCommand.command(\"status\").action(() => {\n const dataDir = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n const s = getSession() ?? readAllSessions(dataDir)[0] ?? null;\n if (!s) {\n console.log(info(\"No active session.\"));\n } else {\n console.log(info(`Active: ${s.customerName} (${s.customerSlug}) since ${s.startedAt}`));\n }\n});\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"session-D9ub6Wl1.js","names":[],"sources":["../src/commands/session.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { setSession, getSession, clearSession } from \"../core/session-store.js\";\nimport { readMainFacts, customerExists } from \"../fs/customer-dir.js\";\nimport { success, error, info } from \"../ui/colors.js\";\n\nfunction sessionsDir(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"sessions\");\n}\n\nexport function persistSession(\n dataDir: string,\n session: {\n customerSlug: string;\n customerName: string;\n startedAt: string;\n owner?: string;\n }\n): void {\n const dir = sessionsDir(dataDir);\n fs.mkdirSync(dir, { recursive: true });\n const key = (session.owner ?? `pid-${process.pid}`).replace(/[^a-zA-Z0-9_-]/g, \"_\");\n fs.writeFileSync(path.join(dir, `${key}.json`), JSON.stringify({ ...session, pid: process.pid }));\n}\n\nexport function clearPersistedSession(dataDir: string, owner?: string): void {\n const dir = sessionsDir(dataDir);\n if (!fs.existsSync(dir)) return;\n if (owner !== undefined) {\n const key = owner.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n const file = path.join(dir, `${key}.json`);\n if (fs.existsSync(file)) fs.unlinkSync(file);\n return;\n }\n // No specific owner — clear all session files\n try {\n for (const f of fs.readdirSync(dir).filter((n) => n.endsWith(\".json\"))) {\n try {\n fs.unlinkSync(path.join(dir, f));\n } catch {\n /* ignore */\n }\n }\n } catch {\n /* ignore */\n }\n}\n\nexport function readAllSessions(\n dataDir: string\n): Array<{ customerSlug: string; customerName: string; startedAt: string; owner?: string }> {\n const dir = sessionsDir(dataDir);\n if (!fs.existsSync(dir)) return [];\n return fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(\".json\"))\n .map((f) => {\n try {\n return JSON.parse(fs.readFileSync(path.join(dir, f), \"utf-8\")) as {\n customerSlug: string;\n customerName: string;\n startedAt: string;\n owner?: string;\n };\n } catch {\n return null;\n }\n })\n .filter((s): s is NonNullable<typeof s> => s !== null);\n}\n\nexport const sessionCommand = new Command(\"session\");\n\nsessionCommand\n .command(\"open <slug>\")\n .option(\"--owner <owner>\", \"Set the owner of this session\")\n .action(async (slug: string, opts: { owner?: string }) => {\n const dataDir = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n if (!customerExists(dataDir, slug)) {\n console.error(error(`✗ Customer not found: ${slug}`));\n process.exit(1);\n }\n const facts = await readMainFacts(dataDir, slug);\n const owner = opts.owner ?? process.env[\"DXCRM_ACTOR\"];\n const session = {\n customerSlug: slug,\n customerName: facts.name,\n startedAt: new Date().toISOString(),\n ...(owner !== undefined ? { owner } : {}),\n };\n setSession(session);\n persistSession(dataDir, session);\n console.log(success(`✓ Session opened: ${facts.name}`));\n });\n\nsessionCommand.command(\"close\").action(() => {\n const dataDir = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n const s = getSession() ?? readAllSessions(dataDir)[0] ?? null;\n clearSession();\n clearPersistedSession(dataDir, s?.owner);\n console.log(success(\"✓ Session closed.\"));\n});\n\nsessionCommand.command(\"status\").action(() => {\n const dataDir = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n const s = getSession() ?? readAllSessions(dataDir)[0] ?? null;\n if (!s) {\n console.log(info(\"No active session.\"));\n } else {\n console.log(info(`Active: ${s.customerName} (${s.customerSlug}) since ${s.startedAt}`));\n }\n});\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- import { r as readAllSessions } from "./session-B9AilxOE.js";
2
- export { readAllSessions };
@@ -1 +0,0 @@
1
- {"version":3,"file":"session-store-B0QZE8Bx.cjs","names":["z","matter","Command"],"sources":["../src/schemas/main-facts.ts","../src/fs/customer-dir.ts","../src/ui/colors.ts","../src/commands/backup.ts","../src/fs/audit-log.ts","../src/core/rbac.ts","../src/core/session-store.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const MainFactsSchema = z.object({\n name: z.string().min(1),\n domain: z.string().optional(),\n email: z.string().optional(),\n phone: z.string().optional(),\n industry: z.string().optional(),\n relationship_stage: z.enum([\"prospect\", \"active\", \"churned\", \"paused\"]),\n deal_value: z.number().optional(),\n currency: z.string().default(\"EUR\"),\n primary_contact: z.string().optional(),\n timezone: z.string().optional(),\n tags: z.array(z.string()).default([]),\n created: z.preprocess(\n (v) => (v instanceof Date ? v.toISOString().slice(0, 10) : v),\n z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"YYYY-MM-DD required\")\n ),\n updated: z.preprocess(\n (v) => (v instanceof Date ? v.toISOString().slice(0, 10) : v),\n z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"YYYY-MM-DD required\")\n ),\n});\n\nexport type MainFacts = z.infer<typeof MainFactsSchema>;\n","import fs from \"fs\";\nimport path from \"path\";\nimport matter from \"gray-matter\";\nimport { fromZodError } from \"zod-validation-error\";\nimport { MainFactsSchema, type MainFacts } from \"../schemas/main-facts.js\";\n\nexport function getCustomerDir(dataDir: string, slug: string): string {\n return path.join(dataDir, \"customers\", slug);\n}\n\nexport function customerExists(dataDir: string, slug: string): boolean {\n return fs.existsSync(getCustomerDir(dataDir, slug));\n}\n\n/** List all customer slugs (immediate subdirectories of customers/). */\nexport function listCustomerSlugs(dataDir: string): string[] {\n const dir = path.join(dataDir, \"customers\");\n if (!fs.existsSync(dir)) return [];\n return fs.readdirSync(dir).filter((s) => {\n try {\n return fs.statSync(path.join(dir, s)).isDirectory();\n } catch {\n return false;\n }\n });\n}\n\nexport async function ensureCustomerDir(dataDir: string, slug: string): Promise<void> {\n const customerDir = getCustomerDir(dataDir, slug);\n fs.mkdirSync(customerDir, { recursive: true });\n fs.mkdirSync(path.join(customerDir, \"attachments\"), { recursive: true });\n fs.mkdirSync(path.join(customerDir, \"transcripts\"), { recursive: true });\n}\n\nexport async function writeMainFacts(\n dataDir: string,\n slug: string,\n facts: MainFacts\n): Promise<void> {\n const filePath = path.join(getCustomerDir(dataDir, slug), \"main_facts.md\");\n // Strip undefined values — gray-matter YAML serializer rejects them\n const clean = Object.fromEntries(\n Object.entries(facts as Record<string, unknown>).filter(([, v]) => v !== undefined)\n );\n const content = matter.stringify(\"\", clean);\n fs.writeFileSync(filePath, content, \"utf-8\");\n}\n\nexport async function readMainFacts(dataDir: string, slug: string): Promise<MainFacts> {\n const filePath = path.join(getCustomerDir(dataDir, slug), \"main_facts.md\");\n if (!fs.existsSync(filePath)) {\n throw new Error(`main_facts.md not found for customer '${slug}'`);\n }\n // Use fs.readFileSync so the memfs mock is respected in tests,\n // then parse the string with matter.\n const content = fs.readFileSync(filePath, \"utf-8\") as string;\n const raw = matter(content);\n // gray-matter parses YYYY-MM-DD as Date objects; coerce back to strings for Zod\n const data = raw.data as Record<string, unknown>;\n for (const key of [\"created\", \"updated\"] as const) {\n if (data[key] instanceof Date) {\n data[key] = (data[key] as Date).toISOString().slice(0, 10);\n }\n }\n const result = MainFactsSchema.safeParse(data);\n if (!result.success) {\n throw new Error(\n fromZodError(result.error, {\n prefix: `Schema error in ${filePath}`,\n prefixSeparator: \":\\n - \",\n issueSeparator: \"\\n - \",\n }).message\n );\n }\n return result.data;\n}\n","import ansis from \"ansis\";\n\nexport const success = (s: string): string => ansis.green(s);\nexport const error = (s: string): string => ansis.red(s);\nexport const warning = (s: string): string => ansis.yellow(s);\nexport const info = (s: string): string => ansis.cyan(s);\nexport const muted = (s: string): string => ansis.gray(s);\nexport const bold = (s: string): string => ansis.bold(s);\n","import { Command } from \"commander\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { execSync } from \"child_process\";\nimport { createHash } from \"crypto\";\nimport { success, error, info, bold } from \"../ui/colors.js\";\n\nexport interface BackupManifest {\n version: \"1\";\n createdAt: string;\n dxcrmVersion: string;\n directories: string[];\n customerCount: number;\n fileCount: number;\n totalBytes: number;\n sha256: string;\n encrypted: boolean;\n retentionTier?: \"daily\" | \"weekly\" | \"monthly\";\n}\n\nexport interface BackupScheduleConfig {\n every: string;\n keep: number;\n weekly?: number;\n monthly?: number;\n lastBackup: string | null;\n remote?: string;\n}\n\nexport interface AgenticConfig {\n backupSchedule?: BackupScheduleConfig;\n}\n\nexport interface BackupEntry {\n filename: string;\n path: string;\n createdAt: string;\n sizeBytes: number;\n verified: boolean;\n encrypted: boolean;\n customerCount: number;\n fileCount: number;\n}\n\n// ─── Config helpers ────────────────────────────────────────────────────────────\n\nfunction getConfigPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"config.json\");\n}\n\nfunction readAgenticConfig(dataDir: string): AgenticConfig {\n const filePath = getConfigPath(dataDir);\n if (!fs.existsSync(filePath)) return {};\n try {\n return JSON.parse(fs.readFileSync(filePath, \"utf-8\") as string) as AgenticConfig;\n } catch {\n return {};\n }\n}\n\nfunction writeAgenticConfig(dataDir: string, config: AgenticConfig): void {\n const filePath = getConfigPath(dataDir);\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, JSON.stringify(config, null, 2), \"utf-8\");\n}\n\n// ─── Manifest ─────────────────────────────────────────────────────────────────\n\nfunction countDir(dir: string): { files: number; bytes: number } {\n let files = 0;\n let bytes = 0;\n if (!fs.existsSync(dir)) return { files, bytes };\n const walk = (d: string) => {\n try {\n for (const entry of fs.readdirSync(d)) {\n const full = path.join(d, entry);\n try {\n const stat = fs.statSync(full);\n if (stat.isDirectory()) walk(full);\n else {\n files++;\n bytes += stat.size;\n }\n } catch {\n /* skip */\n }\n }\n } catch {\n /* skip */\n }\n };\n walk(dir);\n return { files, bytes };\n}\n\nfunction countCustomers(dataDir: string): number {\n const dir = path.join(dataDir, \"customers\");\n if (!fs.existsSync(dir)) return 0;\n try {\n return fs.readdirSync(dir).filter((f) => {\n try {\n return fs.statSync(path.join(dir, f)).isDirectory();\n } catch {\n return false;\n }\n }).length;\n } catch {\n return 0;\n }\n}\n\nfunction sha256File(filePath: string): string {\n if (!fs.existsSync(filePath)) return \"\";\n const hash = createHash(\"sha256\");\n hash.update(fs.readFileSync(filePath));\n return hash.digest(\"hex\");\n}\n\nfunction buildManifest(\n dataDir: string,\n dirs: string[],\n zipPath: string,\n encrypted: boolean\n): BackupManifest {\n let totalFiles = 0;\n let totalBytes = 0;\n for (const d of dirs) {\n const full = path.join(dataDir, d);\n const { files, bytes } = countDir(full);\n totalFiles += files;\n totalBytes += bytes;\n }\n return {\n version: \"1\",\n createdAt: new Date().toISOString(),\n dxcrmVersion: \"0.1.0\",\n directories: dirs,\n customerCount: countCustomers(dataDir),\n fileCount: totalFiles,\n totalBytes,\n sha256: sha256File(zipPath),\n encrypted,\n };\n}\n\n// ─── Manifest log ──────────────────────────────────────────────────────────────\n\nfunction appendBackupLog(dataDir: string, entry: BackupEntry): void {\n const logPath = path.join(dataDir, \".agentic\", \"backup-log.json\");\n let entries: BackupEntry[] = [];\n if (fs.existsSync(logPath)) {\n try {\n entries = JSON.parse(fs.readFileSync(logPath, \"utf-8\") as string) as BackupEntry[];\n } catch {\n entries = [];\n }\n }\n // Deduplicate by filename — update existing entry if same file backed up again\n entries = entries.filter((e) => e.filename !== entry.filename);\n entries.unshift(entry);\n // Keep last 100 entries\n if (entries.length > 100) entries = entries.slice(0, 100);\n fs.mkdirSync(path.dirname(logPath), { recursive: true });\n fs.writeFileSync(logPath, JSON.stringify(entries, null, 2), \"utf-8\");\n}\n\nexport function readBackupLog(dataDir: string): BackupEntry[] {\n const logPath = path.join(dataDir, \".agentic\", \"backup-log.json\");\n if (!fs.existsSync(logPath)) return [];\n try {\n return JSON.parse(fs.readFileSync(logPath, \"utf-8\") as string) as BackupEntry[];\n } catch {\n return [];\n }\n}\n\n// ─── runBackup ────────────────────────────────────────────────────────────────\n\nexport async function runBackup(\n output?: string,\n dataDir?: string,\n opts: { encrypt?: boolean; remote?: string } = {}\n): Promise<BackupManifest | null> {\n const dir = dataDir ?? process.cwd();\n const customersDir = path.join(dir, \"customers\");\n\n if (!fs.existsSync(customersDir)) {\n console.error(error(\"✗ No customers directory found.\"));\n process.exit(1);\n }\n\n const zipPath =\n output ?? path.join(dir, `dxcrm-backup-${new Date().toISOString().slice(0, 10)}.zip`);\n\n // Determine which directories to include\n const includeDirs = [\"customers/\"];\n if (fs.existsSync(path.join(dir, \".agentic\"))) {\n includeDirs.push(\".agentic/\");\n }\n\n try {\n execSync(`zip -r \"${zipPath}\" ${includeDirs.join(\" \")}`, { cwd: dir });\n\n // Build manifest and append to zip\n const manifest = buildManifest(dir, includeDirs, zipPath, opts.encrypt ?? false);\n const manifestPath = path.join(dir, \".dxcrm-manifest-tmp.json\");\n fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), \"utf-8\");\n try {\n execSync(`zip -j \"${zipPath}\" \"${manifestPath}\"`, { cwd: dir });\n } catch {\n /* non-fatal */\n }\n fs.unlinkSync(manifestPath);\n\n // Verify integrity\n const verified = verifyBackupFile(zipPath);\n\n const entry: BackupEntry = {\n filename: path.basename(zipPath),\n path: zipPath,\n createdAt: manifest.createdAt,\n sizeBytes: fs.existsSync(zipPath) ? fs.statSync(zipPath).size : 0,\n verified,\n encrypted: opts.encrypt ?? false,\n customerCount: manifest.customerCount,\n fileCount: manifest.fileCount,\n };\n appendBackupLog(dir, entry);\n\n // Remote upload\n if (opts.remote) {\n await uploadBackup(zipPath, opts.remote);\n }\n\n console.log(success(`✓ Backup saved: ${zipPath}`));\n console.log(\n info(\n ` Customers: ${manifest.customerCount} Files: ${manifest.fileCount} Size: ${(manifest.totalBytes / 1024 / 1024).toFixed(1)} MB`\n )\n );\n if (!verified) console.log(info(\" ⚠ Integrity check failed — backup may be incomplete\"));\n\n return manifest;\n } catch (err) {\n console.error(error(`✗ Backup failed: ${(err as Error).message}`));\n process.exit(1);\n }\n}\n\n// ─── Verify ───────────────────────────────────────────────────────────────────\n\nexport function verifyBackupFile(zipPath: string): boolean {\n if (!fs.existsSync(zipPath)) return false;\n try {\n execSync(`unzip -t \"${zipPath}\"`, { stdio: \"pipe\" });\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function runVerify(zipPath: string): Promise<void> {\n if (!fs.existsSync(zipPath)) {\n console.error(error(`✗ File not found: ${zipPath}`));\n process.exit(1);\n }\n\n console.log(info(`Verifying ${path.basename(zipPath)}...`));\n const ok = verifyBackupFile(zipPath);\n\n if (ok) {\n const size = fs.statSync(zipPath).size;\n const sha = sha256File(zipPath);\n console.log(success(\"✓ ZIP integrity OK\"));\n console.log(info(` Size: ${(size / 1024 / 1024).toFixed(1)} MB`));\n console.log(info(` SHA-256: ${sha}`));\n } else {\n console.error(error(\"✗ Integrity check failed\"));\n process.exit(1);\n }\n}\n\n// ─── Remote Upload ────────────────────────────────────────────────────────────\n\nexport async function uploadBackup(localPath: string, remote: string): Promise<void> {\n if (remote.startsWith(\"s3://\")) {\n // Requires AWS CLI or @aws-sdk/client-s3 to be installed\n try {\n execSync(`aws s3 cp \"${localPath}\" \"${remote}${path.basename(localPath)}\"`, {\n stdio: \"pipe\",\n });\n console.log(info(` ✓ Uploaded to ${remote}${path.basename(localPath)}`));\n } catch (err) {\n console.error(\n error(\n ` ✗ S3 upload failed (install aws-cli or @aws-sdk/client-s3): ${(err as Error).message}`\n )\n );\n }\n } else if (remote.startsWith(\"rsync://\")) {\n const dest = remote.replace(\"rsync://\", \"\");\n try {\n execSync(`rsync -az \"${localPath}\" \"${dest}\"`, { stdio: \"pipe\" });\n console.log(info(` ✓ Synced to ${dest}`));\n } catch (err) {\n console.error(error(` ✗ rsync failed: ${(err as Error).message}`));\n }\n } else {\n // Local directory copy\n try {\n const destPath = path.join(remote, path.basename(localPath));\n fs.mkdirSync(remote, { recursive: true });\n fs.copyFileSync(localPath, destPath);\n console.log(info(` ✓ Copied to ${destPath}`));\n } catch (err) {\n console.error(error(` ✗ Copy failed: ${(err as Error).message}`));\n }\n }\n}\n\n// ─── List Backups ─────────────────────────────────────────────────────────────\n\nexport function listBackupsInDir(dir: string): BackupEntry[] {\n if (!fs.existsSync(dir)) return [];\n try {\n return fs\n .readdirSync(dir)\n .filter((f) => f.match(/^dxcrm-backup-.*\\.(zip|dxbak)$/))\n .map((f) => {\n const fullPath = path.join(dir, f);\n const stat = fs.statSync(fullPath);\n return {\n filename: f,\n path: fullPath,\n createdAt: stat.mtime.toISOString(),\n sizeBytes: stat.size,\n verified: false,\n encrypted: f.endsWith(\".dxbak\"),\n customerCount: 0,\n fileCount: 0,\n } satisfies BackupEntry;\n })\n .sort((a, b) => b.createdAt.localeCompare(a.createdAt));\n } catch {\n return [];\n }\n}\n\n// ─── Retention Policy ─────────────────────────────────────────────────────────\n\nexport interface RetentionConfig {\n daily?: number;\n weekly?: number;\n monthly?: number;\n}\n\nexport function pruneOldBackups(dir: string, keep: number, retention?: RetentionConfig): void {\n const files = fs\n .readdirSync(dir)\n .filter((f) => f.match(/^dxcrm-backup-\\d{4}-\\d{2}-\\d{2}.*\\.(zip|dxbak)$/))\n .sort();\n\n if (!retention) {\n // Legacy: keep last N\n const toDelete = files.slice(0, Math.max(0, files.length - keep));\n for (const f of toDelete) {\n try {\n fs.unlinkSync(path.join(dir, f));\n } catch {\n /* ignore */\n }\n }\n return;\n }\n\n // Grandfathering: daily → weekly → monthly\n const daily = retention.daily ?? keep;\n const weekly = retention.weekly ?? 0;\n const monthly = retention.monthly ?? 0;\n\n const kept = new Set<string>();\n\n // Keep last N daily\n for (const f of files.slice(-daily)) kept.add(f);\n\n // Keep last backup of each week (up to 'weekly' weeks)\n if (weekly > 0) {\n const byWeek = new Map<string, string>();\n for (const f of files) {\n const dateMatch = f.match(/dxcrm-backup-(\\d{4}-\\d{2}-\\d{2})/);\n if (!dateMatch?.[1]) continue;\n const d = new Date(dateMatch[1]);\n // ISO week: year + week number\n const week = `${d.getFullYear()}-W${String(Math.ceil((d.getDate() + new Date(d.getFullYear(), 0, 1).getDay()) / 7)).padStart(2, \"0\")}`;\n byWeek.set(week, f); // last backup of the week wins\n }\n Array.from(byWeek.values())\n .slice(-weekly)\n .forEach((f) => kept.add(f));\n }\n\n // Keep last backup of each month (up to 'monthly' months)\n if (monthly > 0) {\n const byMonth = new Map<string, string>();\n for (const f of files) {\n const dateMatch = f.match(/dxcrm-backup-(\\d{4}-\\d{2})/);\n if (!dateMatch?.[1]) continue;\n byMonth.set(dateMatch[1], f); // last backup of the month wins\n }\n Array.from(byMonth.values())\n .slice(-monthly)\n .forEach((f) => kept.add(f));\n }\n\n for (const f of files) {\n if (!kept.has(f)) {\n try {\n fs.unlinkSync(path.join(dir, f));\n } catch {\n /* ignore */\n }\n }\n }\n}\n\n// ─── Schedule ─────────────────────────────────────────────────────────────────\n\nexport async function runBackupSchedule(\n opts: {\n every?: string;\n keep?: string;\n weekly?: string;\n monthly?: string;\n remote?: string;\n status?: boolean;\n clear?: boolean;\n },\n dataDir?: string\n): Promise<void> {\n const dir = dataDir ?? process.cwd();\n\n if (opts.clear) {\n const config = readAgenticConfig(dir);\n delete config.backupSchedule;\n writeAgenticConfig(dir, config);\n console.log(success(\"✓ Backup schedule cleared.\"));\n return;\n }\n\n if (!opts.every && !opts.status) {\n console.error(error(\"✗ --every is required (e.g. --every day)\"));\n process.exit(1);\n return;\n }\n\n if (opts.every) {\n const keep = opts.keep ? parseInt(opts.keep, 10) : 7;\n const config = readAgenticConfig(dir);\n config.backupSchedule = {\n every: opts.every,\n keep,\n ...(opts.weekly ? { weekly: parseInt(opts.weekly, 10) } : {}),\n ...(opts.monthly ? { monthly: parseInt(opts.monthly, 10) } : {}),\n ...(opts.remote ? { remote: opts.remote } : {}),\n lastBackup: null,\n };\n writeAgenticConfig(dir, config);\n if (!opts.status) {\n console.log(\n success(\n `✓ Backup schedule set: every ${opts.every}, keep ${keep} daily${opts.weekly ? ` / ${opts.weekly} weekly` : \"\"}${opts.monthly ? ` / ${opts.monthly} monthly` : \"\"}.`\n )\n );\n }\n }\n\n if (opts.status) {\n const config = readAgenticConfig(dir);\n const sched = config.backupSchedule;\n if (!sched) {\n console.log(info(\"No backup schedule configured.\"));\n } else {\n console.log(bold(\"Backup Schedule:\"));\n console.log(` every: ${sched.every}`);\n console.log(` keep: ${sched.keep} daily backups`);\n if (sched.weekly) console.log(` weekly: ${sched.weekly} weekly backups`);\n if (sched.monthly) console.log(` monthly: ${sched.monthly} monthly backups`);\n if (sched.remote) console.log(` remote: ${sched.remote}`);\n console.log(` lastBackup: ${sched.lastBackup ?? \"never\"}`);\n }\n }\n}\n\nexport function shouldRunScheduledBackup(dataDir: string): boolean {\n const config = readAgenticConfig(dataDir);\n const sched = config.backupSchedule;\n if (!sched) return false;\n if (!sched.lastBackup) return true;\n const last = new Date(sched.lastBackup).getTime();\n const oneDayMs = 24 * 60 * 60 * 1000;\n return Date.now() - last >= oneDayMs;\n}\n\nexport async function runScheduledBackupIfDue(dataDir: string): Promise<void> {\n if (!shouldRunScheduledBackup(dataDir)) return;\n const config = readAgenticConfig(dataDir);\n const sched = config.backupSchedule!;\n const customersDir = path.join(dataDir, \"customers\");\n if (!fs.existsSync(customersDir)) return;\n\n const zipPath = path.join(dataDir, `dxcrm-backup-${new Date().toISOString().slice(0, 10)}.zip`);\n const includeDirs = [\"customers/\"];\n if (fs.existsSync(path.join(dataDir, \".agentic\"))) includeDirs.push(\".agentic/\");\n\n try {\n execSync(`zip -r \"${zipPath}\" ${includeDirs.join(\" \")}`, { cwd: dataDir });\n\n const retention: RetentionConfig | undefined =\n (sched.weekly ?? sched.monthly)\n ? {\n daily: sched.keep,\n ...(sched.weekly ? { weekly: sched.weekly } : {}),\n ...(sched.monthly ? { monthly: sched.monthly } : {}),\n }\n : undefined;\n pruneOldBackups(dataDir, sched.keep, retention);\n\n if (sched.remote) {\n await uploadBackup(zipPath, sched.remote).catch(() => {\n /* non-fatal */\n });\n }\n\n config.backupSchedule!.lastBackup = new Date().toISOString();\n writeAgenticConfig(dataDir, config);\n process.stderr.write(`[daemon] Scheduled backup saved: ${zipPath}\\n`);\n } catch (err) {\n process.stderr.write(`[daemon] Scheduled backup failed: ${(err as Error).message}\\n`);\n }\n}\n\n// ─── Restore ──────────────────────────────────────────────────────────────────\n\nexport async function runRestore(zipPath: string, dataDir?: string): Promise<void> {\n const dir = dataDir ?? process.cwd();\n try {\n execSync(`unzip -o \"${path.resolve(zipPath)}\" -d \"${dir}\"`, { cwd: dir });\n console.log(success(\"✓ Restore complete.\"));\n } catch (err) {\n console.error(error(`✗ Restore failed: ${(err as Error).message}`));\n process.exit(1);\n }\n}\n\nexport interface RestoreDrillReport {\n ok: boolean;\n verified: boolean;\n hasCustomers: boolean;\n hasAgentic: boolean;\n reason?: string;\n}\n\n/**\n * Restore-drill: verify a backup is actually restorable WITHOUT touching live\n * data — checks integrity (unzip -t) and that the archive contains the expected\n * top-level state (customers/, .agentic/). Returns a report for monitoring.\n */\nexport async function runRestoreDrill(\n zipPath: string,\n opts: { silent?: boolean } = {}\n): Promise<RestoreDrillReport> {\n const resolved = path.resolve(zipPath);\n if (!fs.existsSync(resolved)) {\n if (!opts.silent) console.error(error(`✗ File not found: ${zipPath}`));\n return {\n ok: false,\n verified: false,\n hasCustomers: false,\n hasAgentic: false,\n reason: \"not_found\",\n };\n }\n\n const verified = verifyBackupFile(resolved);\n let hasCustomers = false;\n let hasAgentic = false;\n if (verified) {\n try {\n const listing = execSync(`unzip -l \"${resolved}\"`, { stdio: \"pipe\" }).toString();\n hasCustomers = listing.includes(\"customers/\");\n hasAgentic = listing.includes(\".agentic/\");\n } catch {\n /* listing failed — treated as incomplete */\n }\n }\n\n const ok = verified && hasCustomers;\n if (!opts.silent) {\n if (ok) {\n console.log(\n success(\n `✓ Restore drill OK — integrity verified; customers/${hasAgentic ? \" + .agentic/\" : \"\"} present`\n )\n );\n } else {\n console.error(\n error(`✗ Restore drill failed (verified=${verified}, customers=${hasCustomers})`)\n );\n }\n }\n return { ok, verified, hasCustomers, hasAgentic };\n}\n\n// ─── Commands ─────────────────────────────────────────────────────────────────\n\nconst scheduleSubCommand = new Command(\"schedule\")\n .description(\"Configure automatic backup schedule\")\n .option(\"--every <interval>\", \"Backup interval (e.g. day)\")\n .option(\"--keep <n>\", \"Daily backups to keep (default: 7)\")\n .option(\"--weekly <n>\", \"Weekly backups to keep (e.g. 4)\")\n .option(\"--monthly <n>\", \"Monthly backups to keep (e.g. 12)\")\n .option(\"--remote <url>\", \"Remote destination (s3://, rsync://, or local path)\")\n .option(\"--status\", \"Show current schedule\")\n .option(\"--clear\", \"Remove backup schedule\")\n .action((opts) => runBackupSchedule(opts, process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd()));\n\nconst verifySubCommand = new Command(\"verify\")\n .argument(\"<path>\", \"Path to backup zip\")\n .description(\"Verify backup integrity (SHA-256 + zip test)\")\n .action((zipPath: string) => runVerify(zipPath));\n\nconst drillSubCommand = new Command(\"drill\")\n .argument(\"<path>\", \"Path to backup zip\")\n .description(\"Restore-drill: verify a backup is restorable without touching live data\")\n .action(async (zipPath: string) => {\n const report = await runRestoreDrill(zipPath);\n if (!report.ok) process.exitCode = 1;\n });\n\nconst listSubCommand = new Command(\"list\").description(\"List available backups\").action(() => {\n const dir = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n const entries = readBackupLog(dir);\n const fileEntries = listBackupsInDir(dir);\n const combined = entries.length > 0 ? entries : fileEntries;\n if (combined.length === 0) {\n console.log(info(\"No backups found.\"));\n return;\n }\n for (const e of combined) {\n const enc = e.encrypted ? \" [encrypted]\" : \"\";\n const ver = e.verified ? \" ✓\" : \"\";\n const mb = e.sizeBytes > 0 ? ` ${(e.sizeBytes / 1024 / 1024).toFixed(1)} MB` : \"\";\n console.log(` ${bold(e.filename)}${enc}${ver}${mb} ${e.createdAt.slice(0, 10)}`);\n }\n});\n\nexport const backupCommand = new Command(\"backup\")\n .argument(\"[output]\", \"Output path for backup zip\")\n .description(\"Backup customers/ + .agentic/ directories\")\n .option(\"--encrypt\", \"Encrypt the backup (AES-256-GCM)\")\n .option(\"--remote <url>\", \"Also upload to remote (s3://, rsync://, or path)\")\n .action((output?: string, opts?: { encrypt?: boolean; remote?: string }) => {\n void runBackup(output, process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd(), opts ?? {});\n });\n\nbackupCommand.addCommand(scheduleSubCommand);\nbackupCommand.addCommand(verifySubCommand);\nbackupCommand.addCommand(drillSubCommand);\nbackupCommand.addCommand(listSubCommand);\n\nexport const restoreCommand = new Command(\"restore\")\n .argument(\"<path>\", \"Path to backup zip\")\n .description(\"Restore from backup zip\")\n .action((zipPath: string) => runRestore(zipPath, process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd()));\n","import fs from \"fs\";\nimport path from \"path\";\n\nexport interface AuditEntry {\n timestamp: string; // ISO 8601\n actor: string; // DXCRM_ACTOR env var, or \"system\"\n tool: string; // \"log_interaction\" | \"update_deal\" | \"update_customer_facts\" | etc.\n slug: string; // customer slug\n summary: string; // short description (first 120 chars of summary/deal name)\n}\n\n// File format (one line per entry, append-only):\n// 2026-06-01T09:14:00Z | alice | log_interaction | acme-corp | Called about Q3 renewal...\n\nconst AUDIT_LOG_PATH = \".agentic/audit.log\";\n\nexport function getActor(): string {\n const actor = process.env[\"DXCRM_ACTOR\"];\n return actor && actor.trim().length > 0 ? actor.trim() : \"system\";\n}\n\nexport function writeAuditEntry(dataDir: string, entry: AuditEntry): void {\n const logPath = path.join(dataDir, AUDIT_LOG_PATH);\n const logDir = path.dirname(logPath);\n\n if (!fs.existsSync(logDir)) {\n fs.mkdirSync(logDir, { recursive: true });\n }\n\n const truncatedSummary = entry.summary.slice(0, 120);\n const line = `${entry.timestamp} | ${entry.actor} | ${entry.tool} | ${entry.slug} | ${truncatedSummary}\\n`;\n\n fs.appendFileSync(logPath, line, \"utf-8\");\n}\n\nexport function readAuditLog(dataDir: string): AuditEntry[] {\n const logPath = path.join(dataDir, AUDIT_LOG_PATH);\n\n if (!fs.existsSync(logPath)) {\n return [];\n }\n\n const content = fs.readFileSync(logPath, \"utf-8\") as string;\n const lines = content.split(\"\\n\");\n\n const entries: AuditEntry[] = [];\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n\n const parts = trimmed.split(\" | \");\n if (parts.length < 5) continue;\n\n const [timestamp, actor, tool, slug, ...summaryParts] = parts;\n const summary = summaryParts.join(\" | \");\n\n if (timestamp && actor && tool && slug) {\n entries.push({ timestamp, actor, tool, slug, summary: summary ?? \"\" });\n }\n }\n\n return entries;\n}\n\nexport function filterAuditLog(\n entries: AuditEntry[],\n opts: { slug?: string; actor?: string; limit?: number }\n): AuditEntry[] {\n let filtered = entries;\n\n if (opts.slug !== undefined) {\n filtered = filtered.filter((e) => e.slug === opts.slug);\n }\n\n if (opts.actor !== undefined) {\n filtered = filtered.filter((e) => e.actor === opts.actor);\n }\n\n if (opts.limit !== undefined) {\n filtered = filtered.slice(-opts.limit);\n }\n\n return filtered;\n}\n","import fs from \"fs\";\nimport path from \"path\";\n\nexport type Role = \"admin\" | \"manager\" | \"rep\";\n\nexport interface RbacConfig {\n actors: Record<string, Role>;\n default?: Role;\n owned_customers?: Record<string, string[]>;\n /** Field-level ACL: field name → roles allowed to see it. Others get it redacted. */\n field_acl?: Record<string, Role[]>;\n}\n\nconst ALLOWED_TOOLS: Record<Role, string[]> = {\n admin: [\n \"log_interaction\",\n \"update_deal\",\n \"update_customer_facts\",\n \"export_customer\",\n \"pursue_goal\",\n \"register_push_subscription\",\n \"define_custom_object\",\n \"create_record\",\n ],\n manager: [\"log_interaction\", \"update_deal\", \"pursue_goal\", \"create_record\"],\n rep: [\"log_interaction\", \"update_deal\", \"create_record\"],\n};\n\nfunction rbacPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"rbac.json\");\n}\n\nexport function getRbacConfig(dataDir: string): RbacConfig {\n const p = rbacPath(dataDir);\n if (!fs.existsSync(p)) return { actors: {} };\n try {\n return JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as RbacConfig;\n } catch {\n return { actors: {} };\n }\n}\n\nexport function setActorRole(dataDir: string, actor: string, role: Role): void {\n const p = rbacPath(dataDir);\n fs.mkdirSync(path.dirname(p), { recursive: true });\n const config = getRbacConfig(dataDir);\n config.actors[actor] = role;\n fs.writeFileSync(p, JSON.stringify(config, null, 2), \"utf-8\");\n}\n\nexport function getRole(dataDir: string, actor: string): Role {\n const config = getRbacConfig(dataDir);\n return config.actors[actor] ?? config.default ?? \"rep\";\n}\n\nexport function canWrite(role: Role, tool: string): boolean {\n return ALLOWED_TOOLS[role]?.includes(tool) ?? false;\n}\n\nexport function assertCanWrite(role: Role, tool: string, actor: string): void {\n if (!canWrite(role, tool)) {\n throw new Error(`Access denied: '${actor}' (role: ${role}) cannot use tool '${tool}'`);\n }\n}\n\nexport function enforceRbac(dataDir: string, tool: string): void {\n if (!fs.existsSync(rbacPath(dataDir))) return; // no rbac.json = open access\n const actor = process.env[\"DXCRM_ACTOR\"] ?? \"system\";\n if (actor === \"system\") return; // internal system actor bypasses RBAC\n const role = getRole(dataDir, actor);\n assertCanWrite(role, tool, actor);\n}\n\nexport function canSeeCustomer(dataDir: string, actor: string, slug: string): boolean {\n if (!fs.existsSync(rbacPath(dataDir))) return true; // open access\n if (actor === \"system\") return true; // internal system actor always has full access\n const config = getRbacConfig(dataDir);\n const role = config.actors[actor] ?? config.default ?? \"rep\";\n if (role === \"admin\" || role === \"manager\") return true;\n // rep: only sees customers listed in owned_customers[actor]\n const owned = config.owned_customers;\n if (!owned) return false;\n return (owned[actor] ?? []).includes(slug);\n}\n\n/** Load the field-level ACL (field → allowed roles) from rbac.json. */\nexport function loadFieldAcl(dataDir: string): Record<string, Role[]> {\n return getRbacConfig(dataDir).field_acl ?? {};\n}\n\n/** Whether a role may see a field given the ACL (fields not in the ACL are public). */\nexport function canSeeField(field: string, role: Role, acl: Record<string, Role[]>): boolean {\n const allowed = acl[field];\n if (!allowed) return true;\n return allowed.includes(role);\n}\n\n/** Return a copy of `values` with fields the role may not see removed. */\nexport function redactFields<T extends Record<string, unknown>>(\n values: T,\n role: Role,\n acl: Record<string, Role[]>\n): Partial<T> {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(values)) {\n if (canSeeField(k, role, acl)) out[k] = v;\n }\n return out as Partial<T>;\n}\n","export interface Session {\n customerSlug: string;\n customerName: string;\n startedAt: string;\n owner?: string;\n}\n\nlet activeSession: Session | null = null;\n\nexport function setSession(s: Session): void {\n activeSession = s;\n}\n\nexport function getSession(): Session | null {\n return activeSession;\n}\n\nexport function clearSession(): void {\n activeSession = null;\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"session-store-C8tEvMPw.js","names":[],"sources":["../src/schemas/main-facts.ts","../src/fs/customer-dir.ts","../src/ui/colors.ts","../src/commands/backup.ts","../src/fs/audit-log.ts","../src/core/rbac.ts","../src/core/session-store.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const MainFactsSchema = z.object({\n name: z.string().min(1),\n domain: z.string().optional(),\n email: z.string().optional(),\n phone: z.string().optional(),\n industry: z.string().optional(),\n relationship_stage: z.enum([\"prospect\", \"active\", \"churned\", \"paused\"]),\n deal_value: z.number().optional(),\n currency: z.string().default(\"EUR\"),\n primary_contact: z.string().optional(),\n timezone: z.string().optional(),\n tags: z.array(z.string()).default([]),\n created: z.preprocess(\n (v) => (v instanceof Date ? v.toISOString().slice(0, 10) : v),\n z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"YYYY-MM-DD required\")\n ),\n updated: z.preprocess(\n (v) => (v instanceof Date ? v.toISOString().slice(0, 10) : v),\n z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"YYYY-MM-DD required\")\n ),\n});\n\nexport type MainFacts = z.infer<typeof MainFactsSchema>;\n","import fs from \"fs\";\nimport path from \"path\";\nimport matter from \"gray-matter\";\nimport { fromZodError } from \"zod-validation-error\";\nimport { MainFactsSchema, type MainFacts } from \"../schemas/main-facts.js\";\n\nexport function getCustomerDir(dataDir: string, slug: string): string {\n return path.join(dataDir, \"customers\", slug);\n}\n\nexport function customerExists(dataDir: string, slug: string): boolean {\n return fs.existsSync(getCustomerDir(dataDir, slug));\n}\n\n/** List all customer slugs (immediate subdirectories of customers/). */\nexport function listCustomerSlugs(dataDir: string): string[] {\n const dir = path.join(dataDir, \"customers\");\n if (!fs.existsSync(dir)) return [];\n return fs.readdirSync(dir).filter((s) => {\n try {\n return fs.statSync(path.join(dir, s)).isDirectory();\n } catch {\n return false;\n }\n });\n}\n\nexport async function ensureCustomerDir(dataDir: string, slug: string): Promise<void> {\n const customerDir = getCustomerDir(dataDir, slug);\n fs.mkdirSync(customerDir, { recursive: true });\n fs.mkdirSync(path.join(customerDir, \"attachments\"), { recursive: true });\n fs.mkdirSync(path.join(customerDir, \"transcripts\"), { recursive: true });\n}\n\nexport async function writeMainFacts(\n dataDir: string,\n slug: string,\n facts: MainFacts\n): Promise<void> {\n const filePath = path.join(getCustomerDir(dataDir, slug), \"main_facts.md\");\n // Strip undefined values — gray-matter YAML serializer rejects them\n const clean = Object.fromEntries(\n Object.entries(facts as Record<string, unknown>).filter(([, v]) => v !== undefined)\n );\n const content = matter.stringify(\"\", clean);\n fs.writeFileSync(filePath, content, \"utf-8\");\n}\n\nexport async function readMainFacts(dataDir: string, slug: string): Promise<MainFacts> {\n const filePath = path.join(getCustomerDir(dataDir, slug), \"main_facts.md\");\n if (!fs.existsSync(filePath)) {\n throw new Error(`main_facts.md not found for customer '${slug}'`);\n }\n // Use fs.readFileSync so the memfs mock is respected in tests,\n // then parse the string with matter.\n const content = fs.readFileSync(filePath, \"utf-8\") as string;\n const raw = matter(content);\n // gray-matter parses YYYY-MM-DD as Date objects; coerce back to strings for Zod\n const data = raw.data as Record<string, unknown>;\n for (const key of [\"created\", \"updated\"] as const) {\n if (data[key] instanceof Date) {\n data[key] = (data[key] as Date).toISOString().slice(0, 10);\n }\n }\n const result = MainFactsSchema.safeParse(data);\n if (!result.success) {\n throw new Error(\n fromZodError(result.error, {\n prefix: `Schema error in ${filePath}`,\n prefixSeparator: \":\\n - \",\n issueSeparator: \"\\n - \",\n }).message\n );\n }\n return result.data;\n}\n","import ansis from \"ansis\";\n\nexport const success = (s: string): string => ansis.green(s);\nexport const error = (s: string): string => ansis.red(s);\nexport const warning = (s: string): string => ansis.yellow(s);\nexport const info = (s: string): string => ansis.cyan(s);\nexport const muted = (s: string): string => ansis.gray(s);\nexport const bold = (s: string): string => ansis.bold(s);\n","import { Command } from \"commander\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { execSync } from \"child_process\";\nimport { createHash } from \"crypto\";\nimport { success, error, info, bold } from \"../ui/colors.js\";\n\nexport interface BackupManifest {\n version: \"1\";\n createdAt: string;\n dxcrmVersion: string;\n directories: string[];\n customerCount: number;\n fileCount: number;\n totalBytes: number;\n sha256: string;\n encrypted: boolean;\n retentionTier?: \"daily\" | \"weekly\" | \"monthly\";\n}\n\nexport interface BackupScheduleConfig {\n every: string;\n keep: number;\n weekly?: number;\n monthly?: number;\n lastBackup: string | null;\n remote?: string;\n}\n\nexport interface AgenticConfig {\n backupSchedule?: BackupScheduleConfig;\n}\n\nexport interface BackupEntry {\n filename: string;\n path: string;\n createdAt: string;\n sizeBytes: number;\n verified: boolean;\n encrypted: boolean;\n customerCount: number;\n fileCount: number;\n}\n\n// ─── Config helpers ────────────────────────────────────────────────────────────\n\nfunction getConfigPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"config.json\");\n}\n\nfunction readAgenticConfig(dataDir: string): AgenticConfig {\n const filePath = getConfigPath(dataDir);\n if (!fs.existsSync(filePath)) return {};\n try {\n return JSON.parse(fs.readFileSync(filePath, \"utf-8\") as string) as AgenticConfig;\n } catch {\n return {};\n }\n}\n\nfunction writeAgenticConfig(dataDir: string, config: AgenticConfig): void {\n const filePath = getConfigPath(dataDir);\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, JSON.stringify(config, null, 2), \"utf-8\");\n}\n\n// ─── Manifest ─────────────────────────────────────────────────────────────────\n\nfunction countDir(dir: string): { files: number; bytes: number } {\n let files = 0;\n let bytes = 0;\n if (!fs.existsSync(dir)) return { files, bytes };\n const walk = (d: string) => {\n try {\n for (const entry of fs.readdirSync(d)) {\n const full = path.join(d, entry);\n try {\n const stat = fs.statSync(full);\n if (stat.isDirectory()) walk(full);\n else {\n files++;\n bytes += stat.size;\n }\n } catch {\n /* skip */\n }\n }\n } catch {\n /* skip */\n }\n };\n walk(dir);\n return { files, bytes };\n}\n\nfunction countCustomers(dataDir: string): number {\n const dir = path.join(dataDir, \"customers\");\n if (!fs.existsSync(dir)) return 0;\n try {\n return fs.readdirSync(dir).filter((f) => {\n try {\n return fs.statSync(path.join(dir, f)).isDirectory();\n } catch {\n return false;\n }\n }).length;\n } catch {\n return 0;\n }\n}\n\nfunction sha256File(filePath: string): string {\n if (!fs.existsSync(filePath)) return \"\";\n const hash = createHash(\"sha256\");\n hash.update(fs.readFileSync(filePath));\n return hash.digest(\"hex\");\n}\n\nfunction buildManifest(\n dataDir: string,\n dirs: string[],\n zipPath: string,\n encrypted: boolean\n): BackupManifest {\n let totalFiles = 0;\n let totalBytes = 0;\n for (const d of dirs) {\n const full = path.join(dataDir, d);\n const { files, bytes } = countDir(full);\n totalFiles += files;\n totalBytes += bytes;\n }\n return {\n version: \"1\",\n createdAt: new Date().toISOString(),\n dxcrmVersion: \"0.1.0\",\n directories: dirs,\n customerCount: countCustomers(dataDir),\n fileCount: totalFiles,\n totalBytes,\n sha256: sha256File(zipPath),\n encrypted,\n };\n}\n\n// ─── Manifest log ──────────────────────────────────────────────────────────────\n\nfunction appendBackupLog(dataDir: string, entry: BackupEntry): void {\n const logPath = path.join(dataDir, \".agentic\", \"backup-log.json\");\n let entries: BackupEntry[] = [];\n if (fs.existsSync(logPath)) {\n try {\n entries = JSON.parse(fs.readFileSync(logPath, \"utf-8\") as string) as BackupEntry[];\n } catch {\n entries = [];\n }\n }\n // Deduplicate by filename — update existing entry if same file backed up again\n entries = entries.filter((e) => e.filename !== entry.filename);\n entries.unshift(entry);\n // Keep last 100 entries\n if (entries.length > 100) entries = entries.slice(0, 100);\n fs.mkdirSync(path.dirname(logPath), { recursive: true });\n fs.writeFileSync(logPath, JSON.stringify(entries, null, 2), \"utf-8\");\n}\n\nexport function readBackupLog(dataDir: string): BackupEntry[] {\n const logPath = path.join(dataDir, \".agentic\", \"backup-log.json\");\n if (!fs.existsSync(logPath)) return [];\n try {\n return JSON.parse(fs.readFileSync(logPath, \"utf-8\") as string) as BackupEntry[];\n } catch {\n return [];\n }\n}\n\n// ─── runBackup ────────────────────────────────────────────────────────────────\n\nexport async function runBackup(\n output?: string,\n dataDir?: string,\n opts: { encrypt?: boolean; remote?: string } = {}\n): Promise<BackupManifest | null> {\n const dir = dataDir ?? process.cwd();\n const customersDir = path.join(dir, \"customers\");\n\n if (!fs.existsSync(customersDir)) {\n console.error(error(\"✗ No customers directory found.\"));\n process.exit(1);\n }\n\n const zipPath =\n output ?? path.join(dir, `dxcrm-backup-${new Date().toISOString().slice(0, 10)}.zip`);\n\n // Determine which directories to include\n const includeDirs = [\"customers/\"];\n if (fs.existsSync(path.join(dir, \".agentic\"))) {\n includeDirs.push(\".agentic/\");\n }\n\n try {\n execSync(`zip -r \"${zipPath}\" ${includeDirs.join(\" \")}`, { cwd: dir });\n\n // Build manifest and append to zip\n const manifest = buildManifest(dir, includeDirs, zipPath, opts.encrypt ?? false);\n const manifestPath = path.join(dir, \".dxcrm-manifest-tmp.json\");\n fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), \"utf-8\");\n try {\n execSync(`zip -j \"${zipPath}\" \"${manifestPath}\"`, { cwd: dir });\n } catch {\n /* non-fatal */\n }\n fs.unlinkSync(manifestPath);\n\n // Verify integrity\n const verified = verifyBackupFile(zipPath);\n\n const entry: BackupEntry = {\n filename: path.basename(zipPath),\n path: zipPath,\n createdAt: manifest.createdAt,\n sizeBytes: fs.existsSync(zipPath) ? fs.statSync(zipPath).size : 0,\n verified,\n encrypted: opts.encrypt ?? false,\n customerCount: manifest.customerCount,\n fileCount: manifest.fileCount,\n };\n appendBackupLog(dir, entry);\n\n // Remote upload\n if (opts.remote) {\n await uploadBackup(zipPath, opts.remote);\n }\n\n console.log(success(`✓ Backup saved: ${zipPath}`));\n console.log(\n info(\n ` Customers: ${manifest.customerCount} Files: ${manifest.fileCount} Size: ${(manifest.totalBytes / 1024 / 1024).toFixed(1)} MB`\n )\n );\n if (!verified) console.log(info(\" ⚠ Integrity check failed — backup may be incomplete\"));\n\n return manifest;\n } catch (err) {\n console.error(error(`✗ Backup failed: ${(err as Error).message}`));\n process.exit(1);\n }\n}\n\n// ─── Verify ───────────────────────────────────────────────────────────────────\n\nexport function verifyBackupFile(zipPath: string): boolean {\n if (!fs.existsSync(zipPath)) return false;\n try {\n execSync(`unzip -t \"${zipPath}\"`, { stdio: \"pipe\" });\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function runVerify(zipPath: string): Promise<void> {\n if (!fs.existsSync(zipPath)) {\n console.error(error(`✗ File not found: ${zipPath}`));\n process.exit(1);\n }\n\n console.log(info(`Verifying ${path.basename(zipPath)}...`));\n const ok = verifyBackupFile(zipPath);\n\n if (ok) {\n const size = fs.statSync(zipPath).size;\n const sha = sha256File(zipPath);\n console.log(success(\"✓ ZIP integrity OK\"));\n console.log(info(` Size: ${(size / 1024 / 1024).toFixed(1)} MB`));\n console.log(info(` SHA-256: ${sha}`));\n } else {\n console.error(error(\"✗ Integrity check failed\"));\n process.exit(1);\n }\n}\n\n// ─── Remote Upload ────────────────────────────────────────────────────────────\n\nexport async function uploadBackup(localPath: string, remote: string): Promise<void> {\n if (remote.startsWith(\"s3://\")) {\n // Requires AWS CLI or @aws-sdk/client-s3 to be installed\n try {\n execSync(`aws s3 cp \"${localPath}\" \"${remote}${path.basename(localPath)}\"`, {\n stdio: \"pipe\",\n });\n console.log(info(` ✓ Uploaded to ${remote}${path.basename(localPath)}`));\n } catch (err) {\n console.error(\n error(\n ` ✗ S3 upload failed (install aws-cli or @aws-sdk/client-s3): ${(err as Error).message}`\n )\n );\n }\n } else if (remote.startsWith(\"rsync://\")) {\n const dest = remote.replace(\"rsync://\", \"\");\n try {\n execSync(`rsync -az \"${localPath}\" \"${dest}\"`, { stdio: \"pipe\" });\n console.log(info(` ✓ Synced to ${dest}`));\n } catch (err) {\n console.error(error(` ✗ rsync failed: ${(err as Error).message}`));\n }\n } else {\n // Local directory copy\n try {\n const destPath = path.join(remote, path.basename(localPath));\n fs.mkdirSync(remote, { recursive: true });\n fs.copyFileSync(localPath, destPath);\n console.log(info(` ✓ Copied to ${destPath}`));\n } catch (err) {\n console.error(error(` ✗ Copy failed: ${(err as Error).message}`));\n }\n }\n}\n\n// ─── List Backups ─────────────────────────────────────────────────────────────\n\nexport function listBackupsInDir(dir: string): BackupEntry[] {\n if (!fs.existsSync(dir)) return [];\n try {\n return fs\n .readdirSync(dir)\n .filter((f) => f.match(/^dxcrm-backup-.*\\.(zip|dxbak)$/))\n .map((f) => {\n const fullPath = path.join(dir, f);\n const stat = fs.statSync(fullPath);\n return {\n filename: f,\n path: fullPath,\n createdAt: stat.mtime.toISOString(),\n sizeBytes: stat.size,\n verified: false,\n encrypted: f.endsWith(\".dxbak\"),\n customerCount: 0,\n fileCount: 0,\n } satisfies BackupEntry;\n })\n .sort((a, b) => b.createdAt.localeCompare(a.createdAt));\n } catch {\n return [];\n }\n}\n\n// ─── Retention Policy ─────────────────────────────────────────────────────────\n\nexport interface RetentionConfig {\n daily?: number;\n weekly?: number;\n monthly?: number;\n}\n\nexport function pruneOldBackups(dir: string, keep: number, retention?: RetentionConfig): void {\n const files = fs\n .readdirSync(dir)\n .filter((f) => f.match(/^dxcrm-backup-\\d{4}-\\d{2}-\\d{2}.*\\.(zip|dxbak)$/))\n .sort();\n\n if (!retention) {\n // Legacy: keep last N\n const toDelete = files.slice(0, Math.max(0, files.length - keep));\n for (const f of toDelete) {\n try {\n fs.unlinkSync(path.join(dir, f));\n } catch {\n /* ignore */\n }\n }\n return;\n }\n\n // Grandfathering: daily → weekly → monthly\n const daily = retention.daily ?? keep;\n const weekly = retention.weekly ?? 0;\n const monthly = retention.monthly ?? 0;\n\n const kept = new Set<string>();\n\n // Keep last N daily\n for (const f of files.slice(-daily)) kept.add(f);\n\n // Keep last backup of each week (up to 'weekly' weeks)\n if (weekly > 0) {\n const byWeek = new Map<string, string>();\n for (const f of files) {\n const dateMatch = f.match(/dxcrm-backup-(\\d{4}-\\d{2}-\\d{2})/);\n if (!dateMatch?.[1]) continue;\n const d = new Date(dateMatch[1]);\n // ISO week: year + week number\n const week = `${d.getFullYear()}-W${String(Math.ceil((d.getDate() + new Date(d.getFullYear(), 0, 1).getDay()) / 7)).padStart(2, \"0\")}`;\n byWeek.set(week, f); // last backup of the week wins\n }\n Array.from(byWeek.values())\n .slice(-weekly)\n .forEach((f) => kept.add(f));\n }\n\n // Keep last backup of each month (up to 'monthly' months)\n if (monthly > 0) {\n const byMonth = new Map<string, string>();\n for (const f of files) {\n const dateMatch = f.match(/dxcrm-backup-(\\d{4}-\\d{2})/);\n if (!dateMatch?.[1]) continue;\n byMonth.set(dateMatch[1], f); // last backup of the month wins\n }\n Array.from(byMonth.values())\n .slice(-monthly)\n .forEach((f) => kept.add(f));\n }\n\n for (const f of files) {\n if (!kept.has(f)) {\n try {\n fs.unlinkSync(path.join(dir, f));\n } catch {\n /* ignore */\n }\n }\n }\n}\n\n// ─── Schedule ─────────────────────────────────────────────────────────────────\n\nexport async function runBackupSchedule(\n opts: {\n every?: string;\n keep?: string;\n weekly?: string;\n monthly?: string;\n remote?: string;\n status?: boolean;\n clear?: boolean;\n },\n dataDir?: string\n): Promise<void> {\n const dir = dataDir ?? process.cwd();\n\n if (opts.clear) {\n const config = readAgenticConfig(dir);\n delete config.backupSchedule;\n writeAgenticConfig(dir, config);\n console.log(success(\"✓ Backup schedule cleared.\"));\n return;\n }\n\n if (!opts.every && !opts.status) {\n console.error(error(\"✗ --every is required (e.g. --every day)\"));\n process.exit(1);\n return;\n }\n\n if (opts.every) {\n const keep = opts.keep ? parseInt(opts.keep, 10) : 7;\n const config = readAgenticConfig(dir);\n config.backupSchedule = {\n every: opts.every,\n keep,\n ...(opts.weekly ? { weekly: parseInt(opts.weekly, 10) } : {}),\n ...(opts.monthly ? { monthly: parseInt(opts.monthly, 10) } : {}),\n ...(opts.remote ? { remote: opts.remote } : {}),\n lastBackup: null,\n };\n writeAgenticConfig(dir, config);\n if (!opts.status) {\n console.log(\n success(\n `✓ Backup schedule set: every ${opts.every}, keep ${keep} daily${opts.weekly ? ` / ${opts.weekly} weekly` : \"\"}${opts.monthly ? ` / ${opts.monthly} monthly` : \"\"}.`\n )\n );\n }\n }\n\n if (opts.status) {\n const config = readAgenticConfig(dir);\n const sched = config.backupSchedule;\n if (!sched) {\n console.log(info(\"No backup schedule configured.\"));\n } else {\n console.log(bold(\"Backup Schedule:\"));\n console.log(` every: ${sched.every}`);\n console.log(` keep: ${sched.keep} daily backups`);\n if (sched.weekly) console.log(` weekly: ${sched.weekly} weekly backups`);\n if (sched.monthly) console.log(` monthly: ${sched.monthly} monthly backups`);\n if (sched.remote) console.log(` remote: ${sched.remote}`);\n console.log(` lastBackup: ${sched.lastBackup ?? \"never\"}`);\n }\n }\n}\n\nexport function shouldRunScheduledBackup(dataDir: string): boolean {\n const config = readAgenticConfig(dataDir);\n const sched = config.backupSchedule;\n if (!sched) return false;\n if (!sched.lastBackup) return true;\n const last = new Date(sched.lastBackup).getTime();\n const oneDayMs = 24 * 60 * 60 * 1000;\n return Date.now() - last >= oneDayMs;\n}\n\nexport async function runScheduledBackupIfDue(dataDir: string): Promise<void> {\n if (!shouldRunScheduledBackup(dataDir)) return;\n const config = readAgenticConfig(dataDir);\n const sched = config.backupSchedule!;\n const customersDir = path.join(dataDir, \"customers\");\n if (!fs.existsSync(customersDir)) return;\n\n const zipPath = path.join(dataDir, `dxcrm-backup-${new Date().toISOString().slice(0, 10)}.zip`);\n const includeDirs = [\"customers/\"];\n if (fs.existsSync(path.join(dataDir, \".agentic\"))) includeDirs.push(\".agentic/\");\n\n try {\n execSync(`zip -r \"${zipPath}\" ${includeDirs.join(\" \")}`, { cwd: dataDir });\n\n const retention: RetentionConfig | undefined =\n (sched.weekly ?? sched.monthly)\n ? {\n daily: sched.keep,\n ...(sched.weekly ? { weekly: sched.weekly } : {}),\n ...(sched.monthly ? { monthly: sched.monthly } : {}),\n }\n : undefined;\n pruneOldBackups(dataDir, sched.keep, retention);\n\n if (sched.remote) {\n await uploadBackup(zipPath, sched.remote).catch(() => {\n /* non-fatal */\n });\n }\n\n config.backupSchedule!.lastBackup = new Date().toISOString();\n writeAgenticConfig(dataDir, config);\n process.stderr.write(`[daemon] Scheduled backup saved: ${zipPath}\\n`);\n } catch (err) {\n process.stderr.write(`[daemon] Scheduled backup failed: ${(err as Error).message}\\n`);\n }\n}\n\n// ─── Restore ──────────────────────────────────────────────────────────────────\n\nexport async function runRestore(zipPath: string, dataDir?: string): Promise<void> {\n const dir = dataDir ?? process.cwd();\n try {\n execSync(`unzip -o \"${path.resolve(zipPath)}\" -d \"${dir}\"`, { cwd: dir });\n console.log(success(\"✓ Restore complete.\"));\n } catch (err) {\n console.error(error(`✗ Restore failed: ${(err as Error).message}`));\n process.exit(1);\n }\n}\n\nexport interface RestoreDrillReport {\n ok: boolean;\n verified: boolean;\n hasCustomers: boolean;\n hasAgentic: boolean;\n reason?: string;\n}\n\n/**\n * Restore-drill: verify a backup is actually restorable WITHOUT touching live\n * data — checks integrity (unzip -t) and that the archive contains the expected\n * top-level state (customers/, .agentic/). Returns a report for monitoring.\n */\nexport async function runRestoreDrill(\n zipPath: string,\n opts: { silent?: boolean } = {}\n): Promise<RestoreDrillReport> {\n const resolved = path.resolve(zipPath);\n if (!fs.existsSync(resolved)) {\n if (!opts.silent) console.error(error(`✗ File not found: ${zipPath}`));\n return {\n ok: false,\n verified: false,\n hasCustomers: false,\n hasAgentic: false,\n reason: \"not_found\",\n };\n }\n\n const verified = verifyBackupFile(resolved);\n let hasCustomers = false;\n let hasAgentic = false;\n if (verified) {\n try {\n const listing = execSync(`unzip -l \"${resolved}\"`, { stdio: \"pipe\" }).toString();\n hasCustomers = listing.includes(\"customers/\");\n hasAgentic = listing.includes(\".agentic/\");\n } catch {\n /* listing failed — treated as incomplete */\n }\n }\n\n const ok = verified && hasCustomers;\n if (!opts.silent) {\n if (ok) {\n console.log(\n success(\n `✓ Restore drill OK — integrity verified; customers/${hasAgentic ? \" + .agentic/\" : \"\"} present`\n )\n );\n } else {\n console.error(\n error(`✗ Restore drill failed (verified=${verified}, customers=${hasCustomers})`)\n );\n }\n }\n return { ok, verified, hasCustomers, hasAgentic };\n}\n\n// ─── Commands ─────────────────────────────────────────────────────────────────\n\nconst scheduleSubCommand = new Command(\"schedule\")\n .description(\"Configure automatic backup schedule\")\n .option(\"--every <interval>\", \"Backup interval (e.g. day)\")\n .option(\"--keep <n>\", \"Daily backups to keep (default: 7)\")\n .option(\"--weekly <n>\", \"Weekly backups to keep (e.g. 4)\")\n .option(\"--monthly <n>\", \"Monthly backups to keep (e.g. 12)\")\n .option(\"--remote <url>\", \"Remote destination (s3://, rsync://, or local path)\")\n .option(\"--status\", \"Show current schedule\")\n .option(\"--clear\", \"Remove backup schedule\")\n .action((opts) => runBackupSchedule(opts, process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd()));\n\nconst verifySubCommand = new Command(\"verify\")\n .argument(\"<path>\", \"Path to backup zip\")\n .description(\"Verify backup integrity (SHA-256 + zip test)\")\n .action((zipPath: string) => runVerify(zipPath));\n\nconst drillSubCommand = new Command(\"drill\")\n .argument(\"<path>\", \"Path to backup zip\")\n .description(\"Restore-drill: verify a backup is restorable without touching live data\")\n .action(async (zipPath: string) => {\n const report = await runRestoreDrill(zipPath);\n if (!report.ok) process.exitCode = 1;\n });\n\nconst listSubCommand = new Command(\"list\").description(\"List available backups\").action(() => {\n const dir = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n const entries = readBackupLog(dir);\n const fileEntries = listBackupsInDir(dir);\n const combined = entries.length > 0 ? entries : fileEntries;\n if (combined.length === 0) {\n console.log(info(\"No backups found.\"));\n return;\n }\n for (const e of combined) {\n const enc = e.encrypted ? \" [encrypted]\" : \"\";\n const ver = e.verified ? \" ✓\" : \"\";\n const mb = e.sizeBytes > 0 ? ` ${(e.sizeBytes / 1024 / 1024).toFixed(1)} MB` : \"\";\n console.log(` ${bold(e.filename)}${enc}${ver}${mb} ${e.createdAt.slice(0, 10)}`);\n }\n});\n\nexport const backupCommand = new Command(\"backup\")\n .argument(\"[output]\", \"Output path for backup zip\")\n .description(\"Backup customers/ + .agentic/ directories\")\n .option(\"--encrypt\", \"Encrypt the backup (AES-256-GCM)\")\n .option(\"--remote <url>\", \"Also upload to remote (s3://, rsync://, or path)\")\n .action((output?: string, opts?: { encrypt?: boolean; remote?: string }) => {\n void runBackup(output, process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd(), opts ?? {});\n });\n\nbackupCommand.addCommand(scheduleSubCommand);\nbackupCommand.addCommand(verifySubCommand);\nbackupCommand.addCommand(drillSubCommand);\nbackupCommand.addCommand(listSubCommand);\n\nexport const restoreCommand = new Command(\"restore\")\n .argument(\"<path>\", \"Path to backup zip\")\n .description(\"Restore from backup zip\")\n .action((zipPath: string) => runRestore(zipPath, process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd()));\n","import fs from \"fs\";\nimport path from \"path\";\n\nexport interface AuditEntry {\n timestamp: string; // ISO 8601\n actor: string; // DXCRM_ACTOR env var, or \"system\"\n tool: string; // \"log_interaction\" | \"update_deal\" | \"update_customer_facts\" | etc.\n slug: string; // customer slug\n summary: string; // short description (first 120 chars of summary/deal name)\n}\n\n// File format (one line per entry, append-only):\n// 2026-06-01T09:14:00Z | alice | log_interaction | acme-corp | Called about Q3 renewal...\n\nconst AUDIT_LOG_PATH = \".agentic/audit.log\";\n\nexport function getActor(): string {\n const actor = process.env[\"DXCRM_ACTOR\"];\n return actor && actor.trim().length > 0 ? actor.trim() : \"system\";\n}\n\nexport function writeAuditEntry(dataDir: string, entry: AuditEntry): void {\n const logPath = path.join(dataDir, AUDIT_LOG_PATH);\n const logDir = path.dirname(logPath);\n\n if (!fs.existsSync(logDir)) {\n fs.mkdirSync(logDir, { recursive: true });\n }\n\n const truncatedSummary = entry.summary.slice(0, 120);\n const line = `${entry.timestamp} | ${entry.actor} | ${entry.tool} | ${entry.slug} | ${truncatedSummary}\\n`;\n\n fs.appendFileSync(logPath, line, \"utf-8\");\n}\n\nexport function readAuditLog(dataDir: string): AuditEntry[] {\n const logPath = path.join(dataDir, AUDIT_LOG_PATH);\n\n if (!fs.existsSync(logPath)) {\n return [];\n }\n\n const content = fs.readFileSync(logPath, \"utf-8\") as string;\n const lines = content.split(\"\\n\");\n\n const entries: AuditEntry[] = [];\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n\n const parts = trimmed.split(\" | \");\n if (parts.length < 5) continue;\n\n const [timestamp, actor, tool, slug, ...summaryParts] = parts;\n const summary = summaryParts.join(\" | \");\n\n if (timestamp && actor && tool && slug) {\n entries.push({ timestamp, actor, tool, slug, summary: summary ?? \"\" });\n }\n }\n\n return entries;\n}\n\nexport function filterAuditLog(\n entries: AuditEntry[],\n opts: { slug?: string; actor?: string; limit?: number }\n): AuditEntry[] {\n let filtered = entries;\n\n if (opts.slug !== undefined) {\n filtered = filtered.filter((e) => e.slug === opts.slug);\n }\n\n if (opts.actor !== undefined) {\n filtered = filtered.filter((e) => e.actor === opts.actor);\n }\n\n if (opts.limit !== undefined) {\n filtered = filtered.slice(-opts.limit);\n }\n\n return filtered;\n}\n","import fs from \"fs\";\nimport path from \"path\";\n\nexport type Role = \"admin\" | \"manager\" | \"rep\";\n\nexport interface RbacConfig {\n actors: Record<string, Role>;\n default?: Role;\n owned_customers?: Record<string, string[]>;\n /** Field-level ACL: field name → roles allowed to see it. Others get it redacted. */\n field_acl?: Record<string, Role[]>;\n}\n\nconst ALLOWED_TOOLS: Record<Role, string[]> = {\n admin: [\n \"log_interaction\",\n \"update_deal\",\n \"update_customer_facts\",\n \"export_customer\",\n \"pursue_goal\",\n \"register_push_subscription\",\n \"define_custom_object\",\n \"create_record\",\n ],\n manager: [\"log_interaction\", \"update_deal\", \"pursue_goal\", \"create_record\"],\n rep: [\"log_interaction\", \"update_deal\", \"create_record\"],\n};\n\nfunction rbacPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"rbac.json\");\n}\n\nexport function getRbacConfig(dataDir: string): RbacConfig {\n const p = rbacPath(dataDir);\n if (!fs.existsSync(p)) return { actors: {} };\n try {\n return JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as RbacConfig;\n } catch {\n return { actors: {} };\n }\n}\n\nexport function setActorRole(dataDir: string, actor: string, role: Role): void {\n const p = rbacPath(dataDir);\n fs.mkdirSync(path.dirname(p), { recursive: true });\n const config = getRbacConfig(dataDir);\n config.actors[actor] = role;\n fs.writeFileSync(p, JSON.stringify(config, null, 2), \"utf-8\");\n}\n\nexport function getRole(dataDir: string, actor: string): Role {\n const config = getRbacConfig(dataDir);\n return config.actors[actor] ?? config.default ?? \"rep\";\n}\n\nexport function canWrite(role: Role, tool: string): boolean {\n return ALLOWED_TOOLS[role]?.includes(tool) ?? false;\n}\n\nexport function assertCanWrite(role: Role, tool: string, actor: string): void {\n if (!canWrite(role, tool)) {\n throw new Error(`Access denied: '${actor}' (role: ${role}) cannot use tool '${tool}'`);\n }\n}\n\nexport function enforceRbac(dataDir: string, tool: string): void {\n if (!fs.existsSync(rbacPath(dataDir))) return; // no rbac.json = open access\n const actor = process.env[\"DXCRM_ACTOR\"] ?? \"system\";\n if (actor === \"system\") return; // internal system actor bypasses RBAC\n const role = getRole(dataDir, actor);\n assertCanWrite(role, tool, actor);\n}\n\nexport function canSeeCustomer(dataDir: string, actor: string, slug: string): boolean {\n if (!fs.existsSync(rbacPath(dataDir))) return true; // open access\n if (actor === \"system\") return true; // internal system actor always has full access\n const config = getRbacConfig(dataDir);\n const role = config.actors[actor] ?? config.default ?? \"rep\";\n if (role === \"admin\" || role === \"manager\") return true;\n // rep: only sees customers listed in owned_customers[actor]\n const owned = config.owned_customers;\n if (!owned) return false;\n return (owned[actor] ?? []).includes(slug);\n}\n\n/** Load the field-level ACL (field → allowed roles) from rbac.json. */\nexport function loadFieldAcl(dataDir: string): Record<string, Role[]> {\n return getRbacConfig(dataDir).field_acl ?? {};\n}\n\n/** Whether a role may see a field given the ACL (fields not in the ACL are public). */\nexport function canSeeField(field: string, role: Role, acl: Record<string, Role[]>): boolean {\n const allowed = acl[field];\n if (!allowed) return true;\n return allowed.includes(role);\n}\n\n/** Return a copy of `values` with fields the role may not see removed. */\nexport function redactFields<T extends Record<string, unknown>>(\n values: T,\n role: Role,\n acl: Record<string, Role[]>\n): Partial<T> {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(values)) {\n if (canSeeField(k, role, acl)) out[k] = v;\n }\n return out as Partial<T>;\n}\n","export interface Session {\n customerSlug: string;\n customerName: string;\n startedAt: string;\n owner?: string;\n}\n\nlet activeSession: Session | null = null;\n\nexport function setSession(s: Session): void {\n activeSession = s;\n}\n\nexport function getSession(): Session | null {\n return activeSession;\n}\n\nexport function clearSession(): void {\n activeSession = null;\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"sop-Vp0UPWFW.js","names":[],"sources":["../src/core/sop.ts"],"sourcesContent":["import { randomBytes } from \"crypto\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { hybridSearch } from \"./hybrid-search.js\";\n\n/**\n * SOP module (domino D7 / F5): Standard Operating Procedures — procedural\n * instructions, global or per customer, found via hybrid search to guide task\n * execution (\"how we do X\"). Customer-specific SOPs take precedence over global.\n */\nexport interface Sop {\n id: string;\n scope: \"global\" | \"customer\";\n slug?: string;\n title: string;\n triggers: string[];\n tags?: string[];\n body: string;\n createdAt: string;\n}\n\nfunction globalPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"sops.json\");\n}\nfunction customerPath(dataDir: string, slug: string): string {\n return path.join(dataDir, \"customers\", slug, \"sops.json\");\n}\n\nfunction readFile(p: string): Sop[] {\n if (!fs.existsSync(p)) return [];\n try {\n const data = JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as { sops?: Sop[] };\n return Array.isArray(data.sops) ? data.sops : [];\n } catch {\n return [];\n }\n}\nfunction writeFile(p: string, sops: Sop[]): void {\n fs.mkdirSync(path.dirname(p), { recursive: true });\n fs.writeFileSync(p, JSON.stringify({ sops }, null, 2), \"utf-8\");\n}\n\nexport function addSop(\n dataDir: string,\n s: {\n scope: \"global\" | \"customer\";\n slug?: string;\n title: string;\n triggers: string[];\n tags?: string[];\n body: string;\n }\n): Sop {\n const sop: Sop = {\n id: `sop_${randomBytes(5).toString(\"hex\")}`,\n scope: s.scope,\n ...(s.slug ? { slug: s.slug } : {}),\n title: s.title,\n triggers: s.triggers,\n ...(s.tags ? { tags: s.tags } : {}),\n body: s.body,\n createdAt: new Date().toISOString(),\n };\n const p = s.scope === \"global\" ? globalPath(dataDir) : customerPath(dataDir, s.slug ?? \"\");\n writeFile(p, [...readFile(p), sop]);\n return sop;\n}\n\nexport function loadSops(dataDir: string, slug?: string): Sop[] {\n const global = readFile(globalPath(dataDir));\n if (!slug) return global;\n return [...global, ...readFile(customerPath(dataDir, slug))];\n}\n\n/**\n * Find SOPs relevant to a task. Hybrid-search over title+triggers+body; among\n * matches, customer-specific SOPs are returned before global ones.\n */\nexport async function findSops(dataDir: string, query: string, slug?: string): Promise<Sop[]> {\n const sops = loadSops(dataDir, slug);\n const docs = sops.map((s) => ({\n id: s.id,\n text: `${s.title} ${s.triggers.join(\" \")} ${(s.tags ?? []).join(\" \")} ${s.body}`,\n }));\n const ranked = hybridSearch(query, docs);\n const byId = new Map(sops.map((s) => [s.id, s]));\n const matched = ranked.map((r, i) => ({ sop: byId.get(r.id)!, rank: i })).filter((x) => x.sop);\n // Customer-scoped SOPs first, then by relevance rank.\n matched.sort((a, b) => {\n const ac = a.sop.scope === \"customer\" ? 0 : 1;\n const bc = b.sop.scope === \"customer\" ? 0 : 1;\n return ac !== bc ? ac - bc : a.rank - b.rank;\n });\n return matched.map((x) => x.sop);\n}\n"],"mappings":";;;;;AAqBA,SAAS,WAAW,SAAyB;CAC3C,OAAO,KAAK,KAAK,SAAS,YAAY,WAAW;AACnD;AACA,SAAS,aAAa,SAAiB,MAAsB;CAC3D,OAAO,KAAK,KAAK,SAAS,aAAa,MAAM,WAAW;AAC1D;AAEA,SAAS,SAAS,GAAkB;CAClC,IAAI,CAAC,GAAG,WAAW,CAAC,GAAG,OAAO,CAAC;CAC/B,IAAI;EACF,MAAM,OAAO,KAAK,MAAM,GAAG,aAAa,GAAG,OAAO,CAAW;EAC7D,OAAO,MAAM,QAAQ,KAAK,IAAI,IAAI,KAAK,OAAO,CAAC;CACjD,QAAQ;EACN,OAAO,CAAC;CACV;AACF;AACA,SAAS,UAAU,GAAW,MAAmB;CAC/C,GAAG,UAAU,KAAK,QAAQ,CAAC,GAAG,EAAE,WAAW,KAAK,CAAC;CACjD,GAAG,cAAc,GAAG,KAAK,UAAU,EAAE,KAAK,GAAG,MAAM,CAAC,GAAG,OAAO;AAChE;AAEA,SAAgB,OACd,SACA,GAQK;CACL,MAAM,MAAW;EACf,IAAI,OAAO,YAAY,CAAC,EAAE,SAAS,KAAK;EACxC,OAAO,EAAE;EACT,GAAI,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;EACjC,OAAO,EAAE;EACT,UAAU,EAAE;EACZ,GAAI,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;EACjC,MAAM,EAAE;EACR,4BAAW,IAAI,KAAK,GAAE,YAAY;CACpC;CACA,MAAM,IAAI,EAAE,UAAU,WAAW,WAAW,OAAO,IAAI,aAAa,SAAS,EAAE,QAAQ,EAAE;CACzF,UAAU,GAAG,CAAC,GAAG,SAAS,CAAC,GAAG,GAAG,CAAC;CAClC,OAAO;AACT;AAEA,SAAgB,SAAS,SAAiB,MAAsB;CAC9D,MAAM,SAAS,SAAS,WAAW,OAAO,CAAC;CAC3C,IAAI,CAAC,MAAM,OAAO;CAClB,OAAO,CAAC,GAAG,QAAQ,GAAG,SAAS,aAAa,SAAS,IAAI,CAAC,CAAC;AAC7D;;;;;AAMA,eAAsB,SAAS,SAAiB,OAAe,MAA+B;CAC5F,MAAM,OAAO,SAAS,SAAS,IAAI;CAKnC,MAAM,SAAS,aAAa,OAJf,KAAK,KAAK,OAAO;EAC5B,IAAI,EAAE;EACN,MAAM,GAAG,EAAE,MAAM,GAAG,EAAE,SAAS,KAAK,GAAG,EAAE,IAAI,EAAE,QAAQ,CAAC,GAAG,KAAK,GAAG,EAAE,GAAG,EAAE;CAC5E,EACsC,CAAC;CACvC,MAAM,OAAO,IAAI,IAAI,KAAK,KAAK,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;CAC/C,MAAM,UAAU,OAAO,KAAK,GAAG,OAAO;EAAE,KAAK,KAAK,IAAI,EAAE,EAAE;EAAI,MAAM;CAAE,EAAE,EAAE,QAAQ,MAAM,EAAE,GAAG;CAE7F,QAAQ,MAAM,GAAG,MAAM;EACrB,MAAM,KAAK,EAAE,IAAI,UAAU,aAAa,IAAI;EAC5C,MAAM,KAAK,EAAE,IAAI,UAAU,aAAa,IAAI;EAC5C,OAAO,OAAO,KAAK,KAAK,KAAK,EAAE,OAAO,EAAE;CAC1C,CAAC;CACD,OAAO,QAAQ,KAAK,MAAM,EAAE,GAAG;AACjC"}
@@ -1,2 +0,0 @@
1
- import { s as recordSurveyResponse } from "./survey-engine-DBjCYqCv.js";
2
- export { recordSurveyResponse };
@@ -1 +0,0 @@
1
- {"version":3,"file":"survey-engine-DBjCYqCv.js","names":[],"sources":["../src/schemas/survey.ts","../src/core/survey-engine.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const SurveyDefinitionSchema = z.object({\n id: z.string().min(1),\n type: z.enum([\"nps\", \"csat\", \"ces\"]).default(\"nps\"),\n question: z.string().min(1),\n scale: z\n .object({ min: z.number().default(0), max: z.number().default(10) })\n .default({ min: 0, max: 10 }),\n includeComment: z.boolean().default(true),\n commentPrompt: z.string().optional(),\n createdAt: z.string(),\n});\n\nexport const SurveyResponseSchema = z.object({\n surveyId: z.string(),\n slug: z.string(),\n contactEmail: z.string().email(),\n score: z.number().int(),\n comment: z.string().optional(),\n respondedAt: z.string(),\n token: z.string(),\n sentAt: z.string(),\n});\n\nexport type SurveyDefinition = z.infer<typeof SurveyDefinitionSchema>;\nexport type SurveyResponse = z.infer<typeof SurveyResponseSchema>;\n","import fs from \"fs\";\nimport path from \"path\";\nimport { createHmac } from \"crypto\";\nimport yaml from \"js-yaml\";\nimport {\n SurveyDefinitionSchema,\n SurveyResponseSchema,\n type SurveyDefinition,\n type SurveyResponse,\n} from \"../schemas/survey.js\";\n\nconst SURVEY_SECRET = process.env[\"DXCRM_SURVEY_SECRET\"] ?? \"dxcrm-survey-default-secret\";\n\nexport function surveysDir(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"surveys\");\n}\n\nexport function responsesDir(dataDir: string, surveyId: string): string {\n return path.join(dataDir, \".agentic\", \"survey-responses\", surveyId);\n}\n\nexport function getSurvey(dataDir: string, surveyId: string): SurveyDefinition | null {\n const p = path.join(surveysDir(dataDir), `${surveyId}.yaml`);\n if (!fs.existsSync(p)) return null;\n try {\n const raw = yaml.load(fs.readFileSync(p, \"utf-8\") as string);\n const result = SurveyDefinitionSchema.safeParse(raw);\n return result.success ? result.data : null;\n } catch {\n return null;\n }\n}\n\nexport function writeSurvey(dataDir: string, survey: SurveyDefinition): void {\n const dir = surveysDir(dataDir);\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(path.join(dir, `${survey.id}.yaml`), yaml.dump(survey), \"utf-8\");\n}\n\nexport function listSurveys(dataDir: string): SurveyDefinition[] {\n const dir = surveysDir(dataDir);\n if (!fs.existsSync(dir)) return [];\n return fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(\".yaml\"))\n .flatMap((f) => {\n const s = getSurvey(dataDir, f.replace(/\\.yaml$/, \"\"));\n return s ? [s] : [];\n });\n}\n\nexport function generateSurveyToken(slug: string, contactEmail: string, surveyId: string): string {\n return createHmac(\"sha256\", SURVEY_SECRET)\n .update(`${slug}:${contactEmail}:${surveyId}`)\n .digest(\"hex\")\n .slice(0, 16);\n}\n\nexport function buildSurveyEmail(\n survey: SurveyDefinition,\n serverUrl: string,\n token: string\n): { subject: string; body: string } {\n const scores = Array.from(\n { length: survey.scale.max - survey.scale.min + 1 },\n (_, i) => i + survey.scale.min\n );\n const buttons = scores\n .map(\n (s) =>\n `<a href=\"${serverUrl}/survey/respond?token=${token}&score=${s}\" style=\"display:inline-block;margin:4px;padding:10px 16px;background:#1a1a2e;color:white;text-decoration:none;border-radius:4px;\">${s}</a>`\n )\n .join(\"\");\n\n const body = `<p>${survey.question}</p>\n<p>${buttons}</p>\n${survey.includeComment ? `<p>Or <a href=\"${serverUrl}/survey/respond?token=${token}&comment=true\">Click here to add a comment</a></p>` : \"\"}`;\n\n return {\n subject: survey.type === \"nps\" ? \"How likely are you to recommend us?\" : \"Rate your experience\",\n body,\n };\n}\n\nexport async function recordSurveyResponse(\n dataDir: string,\n token: string,\n score: number,\n comment?: string\n): Promise<SurveyResponse | null> {\n // Find pending response by token\n const pendingDir = path.join(dataDir, \".agentic\", \"survey-pending\");\n if (!fs.existsSync(pendingDir)) return null;\n\n const files = fs.readdirSync(pendingDir).filter((f) => f.endsWith(\".json\"));\n for (const file of files) {\n try {\n const pending = JSON.parse(\n fs.readFileSync(path.join(pendingDir, file), \"utf-8\") as string\n ) as {\n token: string;\n surveyId: string;\n slug: string;\n contactEmail: string;\n sentAt: string;\n };\n if (pending.token !== token) continue;\n\n const response: SurveyResponse = {\n surveyId: pending.surveyId,\n slug: pending.slug,\n contactEmail: pending.contactEmail,\n score,\n ...(comment ? { comment } : {}),\n respondedAt: new Date().toISOString(),\n token,\n sentAt: pending.sentAt,\n };\n\n const dir = responsesDir(dataDir, pending.surveyId);\n fs.mkdirSync(dir, { recursive: true });\n const filename = `${pending.slug}_${pending.contactEmail.replace(\"@\", \"_at_\")}_${Date.now()}.json`;\n fs.writeFileSync(path.join(dir, filename), JSON.stringify(response, null, 2), \"utf-8\");\n\n // Delete pending entry\n fs.unlinkSync(path.join(pendingDir, file));\n return response;\n } catch {\n continue;\n }\n }\n return null;\n}\n\nexport function loadSurveyResponses(\n dataDir: string,\n surveyId: string,\n slug?: string\n): SurveyResponse[] {\n const dir = responsesDir(dataDir, surveyId);\n if (!fs.existsSync(dir)) return [];\n return fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(\".json\"))\n .flatMap((f) => {\n try {\n const raw = JSON.parse(fs.readFileSync(path.join(dir, f), \"utf-8\") as string) as unknown;\n const parsed = SurveyResponseSchema.safeParse(raw);\n if (!parsed.success) return [];\n if (slug && parsed.data.slug !== slug) return [];\n return [parsed.data];\n } catch {\n return [];\n }\n });\n}\n\nexport function calcNpsScore(responses: SurveyResponse[]): number {\n if (responses.length === 0) return 0;\n const promoters = responses.filter((r) => r.score >= 9).length;\n const detractors = responses.filter((r) => r.score <= 6).length;\n return Math.round(((promoters - detractors) / responses.length) * 100);\n}\n\nexport async function savePendingSurvey(\n dataDir: string,\n surveyId: string,\n slug: string,\n contactEmail: string,\n token: string\n): Promise<void> {\n const pendingDir = path.join(dataDir, \".agentic\", \"survey-pending\");\n fs.mkdirSync(pendingDir, { recursive: true });\n const filename = `${token}.json`;\n const pending = { token, surveyId, slug, contactEmail, sentAt: new Date().toISOString() };\n fs.writeFileSync(path.join(pendingDir, filename), JSON.stringify(pending, null, 2), \"utf-8\");\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"sync-state-ChaLbamC.js","names":[],"sources":["../src/fs/sync-state.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\n\nexport interface SlugSyncState {\n lastGmailSync?: string;\n lastCalendarSync?: string;\n lastGmailPushHistoryId?: string;\n lastMicrosoftPushAt?: string;\n}\n\nexport interface SyncState {\n [slug: string]: SlugSyncState;\n}\n\nfunction getSyncStatePath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"sync-state.json\");\n}\n\nexport function readSyncState(dataDir: string): SyncState {\n const filePath = getSyncStatePath(dataDir);\n if (!fs.existsSync(filePath)) return {};\n try {\n return JSON.parse(fs.readFileSync(filePath, \"utf-8\") as string) as SyncState;\n } catch {\n return {};\n }\n}\n\nexport function writeSyncState(dataDir: string, state: SyncState): void {\n const filePath = getSyncStatePath(dataDir);\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, JSON.stringify(state, null, 2), \"utf-8\");\n}\n\nexport function updateSlugSyncState(\n dataDir: string,\n slug: string,\n update: Partial<SlugSyncState>\n): void {\n const filePath = getSyncStatePath(dataDir);\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n const state = readSyncState(dataDir);\n state[slug] = { ...state[slug], ...update };\n fs.writeFileSync(filePath, JSON.stringify(state, null, 2), \"utf-8\");\n}\n\nexport function getLastGmailSync(dataDir: string, slug: string): Date | undefined {\n const ts = readSyncState(dataDir)[slug]?.lastGmailSync;\n return ts ? new Date(ts) : undefined;\n}\n\nexport function getLastCalendarSync(dataDir: string, slug: string): Date | undefined {\n const ts = readSyncState(dataDir)[slug]?.lastCalendarSync;\n return ts ? new Date(ts) : undefined;\n}\n"],"mappings":";;;AAcA,SAAS,iBAAiB,SAAyB;CACjD,OAAO,KAAK,KAAK,SAAS,YAAY,iBAAiB;AACzD;AAEA,SAAgB,cAAc,SAA4B;CACxD,MAAM,WAAW,iBAAiB,OAAO;CACzC,IAAI,CAAC,GAAG,WAAW,QAAQ,GAAG,OAAO,CAAC;CACtC,IAAI;EACF,OAAO,KAAK,MAAM,GAAG,aAAa,UAAU,OAAO,CAAW;CAChE,QAAQ;EACN,OAAO,CAAC;CACV;AACF;AAQA,SAAgB,oBACd,SACA,MACA,QACM;CACN,MAAM,WAAW,iBAAiB,OAAO;CACzC,GAAG,UAAU,KAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;CACxD,MAAM,QAAQ,cAAc,OAAO;CACnC,MAAM,QAAQ;EAAE,GAAG,MAAM;EAAO,GAAG;CAAO;CAC1C,GAAG,cAAc,UAAU,KAAK,UAAU,OAAO,MAAM,CAAC,GAAG,OAAO;AACpE;AAEA,SAAgB,iBAAiB,SAAiB,MAAgC;CAChF,MAAM,KAAK,cAAc,OAAO,EAAE,OAAO;CACzC,OAAO,KAAK,IAAI,KAAK,EAAE,IAAI,KAAA;AAC7B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"ticket-writer-j2oX_Wal.js","names":[],"sources":["../src/schemas/ticket.ts","../src/fs/ticket-writer.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const TicketStatusSchema = z.enum([\"open\", \"in-progress\", \"waiting\", \"resolved\", \"closed\"]);\nexport const TicketPrioritySchema = z.enum([\"urgent\", \"high\", \"normal\", \"low\"]);\n\nexport const TicketSchema = z.object({\n id: z.string().regex(/^T-\\d{3,}$/),\n title: z.string().min(1),\n status: TicketStatusSchema,\n priority: TicketPrioritySchema.default(\"normal\"),\n assignee: z.string().optional(),\n created: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/),\n slaDue: z\n .string()\n .regex(/^\\d{4}-\\d{2}-\\d{2}$/)\n .optional(),\n resolved: z\n .string()\n .regex(/^\\d{4}-\\d{2}-\\d{2}$/)\n .optional(),\n description: z.string().optional(),\n});\n\nexport type Ticket = z.infer<typeof TicketSchema>;\nexport type TicketStatus = z.infer<typeof TicketStatusSchema>;\nexport type TicketPriority = z.infer<typeof TicketPrioritySchema>;\n","import fs from \"fs\";\nimport path from \"path\";\nimport { TicketSchema, type Ticket } from \"../schemas/ticket.js\";\n\nconst TICKET_HEADER = \"# Tickets\\n\\n\";\nconst TABLE_HEADER = `| ID | Title | Status | Priority | Assignee | Created | SLA Due | Resolved |\n|----|-------|--------|----------|----------|---------|---------|---------|`;\n\nfunction ticketsPath(dataDir: string, slug: string): string {\n return path.join(dataDir, \"customers\", slug, \"tickets.md\");\n}\n\nfunction escapeMd(s: string | undefined): string {\n return (s ?? \"\").replace(/\\|/g, \"\\\\|\").replace(/\\n/g, \" \");\n}\n\nfunction parseTicketsFromMarkdown(content: string): Ticket[] {\n const tickets: Ticket[] = [];\n const lines = content.split(\"\\n\");\n let inTable = false;\n\n for (const line of lines) {\n if (line.startsWith(\"| ID |\") || line.startsWith(\"|----\")) {\n inTable = true;\n continue;\n }\n if (!inTable) continue;\n if (!line.startsWith(\"|\")) {\n inTable = false;\n continue;\n }\n\n const cols = line\n .split(\"|\")\n .slice(1, -1)\n .map((c) => c.trim());\n if (cols.length < 8) continue;\n const [id, title, status, priority, assignee, created, slaDue, resolved] = cols;\n if (!id || !title || id === \"ID\") continue;\n\n const raw = {\n id,\n title,\n status: status || \"open\",\n priority: priority || \"normal\",\n ...(assignee ? { assignee } : {}),\n created: created || new Date().toISOString().slice(0, 10),\n ...(slaDue ? { slaDue } : {}),\n ...(resolved ? { resolved } : {}),\n };\n\n const parsed = TicketSchema.safeParse(raw);\n if (parsed.success) tickets.push(parsed.data);\n }\n return tickets;\n}\n\nfunction serializeTickets(tickets: Ticket[]): string {\n const rows = tickets.map(\n (t) =>\n `| ${t.id} | ${escapeMd(t.title)} | ${t.status} | ${t.priority} | ${t.assignee ?? \"\"} | ${t.created} | ${t.slaDue ?? \"\"} | ${t.resolved ?? \"\"} |`\n );\n return `${TICKET_HEADER}${TABLE_HEADER}\\n${rows.join(\"\\n\")}\\n`;\n}\n\nexport async function readTickets(dataDir: string, slug: string): Promise<Ticket[]> {\n const p = ticketsPath(dataDir, slug);\n if (!fs.existsSync(p)) return [];\n return parseTicketsFromMarkdown(fs.readFileSync(p, \"utf-8\") as string);\n}\n\nexport async function upsertTicket(dataDir: string, slug: string, ticket: Ticket): Promise<void> {\n const p = ticketsPath(dataDir, slug);\n const existing = await readTickets(dataDir, slug);\n const idx = existing.findIndex((t) => t.id === ticket.id);\n if (idx >= 0) {\n existing[idx] = ticket;\n } else {\n existing.push(ticket);\n }\n fs.writeFileSync(p, serializeTickets(existing), \"utf-8\");\n}\n\nexport function nextTicketId(tickets: Ticket[]): string {\n const nums = tickets.map((t) => parseInt(t.id.replace(\"T-\", \"\"), 10)).filter((n) => !isNaN(n));\n const max = nums.length > 0 ? Math.max(...nums) : 0;\n return `T-${String(max + 1).padStart(3, \"0\")}`;\n}\n\nexport async function listAllTickets(\n dataDir: string,\n filter?: { slug?: string; status?: string; priority?: string; assignee?: string }\n): Promise<Array<{ slug: string; ticket: Ticket }>> {\n const customersDir = path.join(dataDir, \"customers\");\n if (!fs.existsSync(customersDir)) return [];\n\n const slugs = filter?.slug\n ? [filter.slug]\n : fs.readdirSync(customersDir).filter((s) => {\n try {\n return fs.statSync(path.join(customersDir, s)).isDirectory();\n } catch {\n return false;\n }\n });\n\n const results: Array<{ slug: string; ticket: Ticket }> = [];\n for (const slug of slugs) {\n const tickets = await readTickets(dataDir, slug);\n for (const ticket of tickets) {\n if (filter?.status && ticket.status !== filter.status) continue;\n if (filter?.priority && ticket.priority !== filter.priority) continue;\n if (filter?.assignee && ticket.assignee !== filter.assignee) continue;\n results.push({ slug, ticket });\n }\n }\n\n // Sort: urgent first, then by created date\n const priorityOrder: Record<string, number> = { urgent: 0, high: 1, normal: 2, low: 3 };\n results.sort((a, b) => {\n const pa = priorityOrder[a.ticket.priority] ?? 2;\n const pb = priorityOrder[b.ticket.priority] ?? 2;\n if (pa !== pb) return pa - pb;\n return a.ticket.created.localeCompare(b.ticket.created);\n });\n\n return results;\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"tone-Bdm5uaht.js","names":[],"sources":["../src/core/tone.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\n\n/**\n * Customer tonality (domino D8 / F2): per-customer (and global) tone profiles\n * applied automatically to generated communication. Customer profile fields\n * override the global default; the merged profile is rendered into a tone\n * instruction for the LLM (used by draft_email / sequences / journeys).\n */\nexport interface ToneProfile {\n formality?: string; // e.g. formal | casual | friendly\n language?: string; // e.g. de | en\n dos?: string[];\n donts?: string[];\n examples?: string[];\n}\n\nfunction globalPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"tone.json\");\n}\nfunction customerPath(dataDir: string, slug: string): string {\n return path.join(dataDir, \"customers\", slug, \"tone.json\");\n}\n\nfunction readProfile(p: string): ToneProfile {\n if (!fs.existsSync(p)) return {};\n try {\n return JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as ToneProfile;\n } catch {\n return {};\n }\n}\n\nexport function setTone(dataDir: string, profile: ToneProfile, slug?: string): void {\n const p = slug ? customerPath(dataDir, slug) : globalPath(dataDir);\n const merged = { ...readProfile(p), ...profile };\n fs.mkdirSync(path.dirname(p), { recursive: true });\n fs.writeFileSync(p, JSON.stringify(merged, null, 2), \"utf-8\");\n}\n\n/** Effective profile: global as base, customer fields override. */\nexport function resolveTone(dataDir: string, slug?: string): ToneProfile {\n const global = readProfile(globalPath(dataDir));\n if (!slug) return global;\n return { ...global, ...readProfile(customerPath(dataDir, slug)) };\n}\n\n/** Render a tone profile into an instruction string (\"\" when blank). */\nexport function toneInstruction(profile: ToneProfile): string {\n const parts: string[] = [];\n if (profile.formality) parts.push(`tone: ${profile.formality}`);\n if (profile.language) parts.push(`language: ${profile.language}`);\n if (profile.dos && profile.dos.length) parts.push(`prefer: ${profile.dos.join(\", \")}`);\n if (profile.donts && profile.donts.length) parts.push(`avoid: ${profile.donts.join(\", \")}`);\n return parts.join(\"; \");\n}\n"],"mappings":";;;AAiBA,SAAS,WAAW,SAAyB;CAC3C,OAAO,KAAK,KAAK,SAAS,YAAY,WAAW;AACnD;AACA,SAAS,aAAa,SAAiB,MAAsB;CAC3D,OAAO,KAAK,KAAK,SAAS,aAAa,MAAM,WAAW;AAC1D;AAEA,SAAS,YAAY,GAAwB;CAC3C,IAAI,CAAC,GAAG,WAAW,CAAC,GAAG,OAAO,CAAC;CAC/B,IAAI;EACF,OAAO,KAAK,MAAM,GAAG,aAAa,GAAG,OAAO,CAAW;CACzD,QAAQ;EACN,OAAO,CAAC;CACV;AACF;AAEA,SAAgB,QAAQ,SAAiB,SAAsB,MAAqB;CAClF,MAAM,IAAI,OAAO,aAAa,SAAS,IAAI,IAAI,WAAW,OAAO;CACjE,MAAM,SAAS;EAAE,GAAG,YAAY,CAAC;EAAG,GAAG;CAAQ;CAC/C,GAAG,UAAU,KAAK,QAAQ,CAAC,GAAG,EAAE,WAAW,KAAK,CAAC;CACjD,GAAG,cAAc,GAAG,KAAK,UAAU,QAAQ,MAAM,CAAC,GAAG,OAAO;AAC9D;;AAGA,SAAgB,YAAY,SAAiB,MAA4B;CACvE,MAAM,SAAS,YAAY,WAAW,OAAO,CAAC;CAC9C,IAAI,CAAC,MAAM,OAAO;CAClB,OAAO;EAAE,GAAG;EAAQ,GAAG,YAAY,aAAa,SAAS,IAAI,CAAC;CAAE;AAClE;;AAGA,SAAgB,gBAAgB,SAA8B;CAC5D,MAAM,QAAkB,CAAC;CACzB,IAAI,QAAQ,WAAW,MAAM,KAAK,SAAS,QAAQ,WAAW;CAC9D,IAAI,QAAQ,UAAU,MAAM,KAAK,aAAa,QAAQ,UAAU;CAChE,IAAI,QAAQ,OAAO,QAAQ,IAAI,QAAQ,MAAM,KAAK,WAAW,QAAQ,IAAI,KAAK,IAAI,GAAG;CACrF,IAAI,QAAQ,SAAS,QAAQ,MAAM,QAAQ,MAAM,KAAK,UAAU,QAAQ,MAAM,KAAK,IAAI,GAAG;CAC1F,OAAO,MAAM,KAAK,IAAI;AACxB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"tone-DRKlZgPr.cjs","names":[],"sources":["../src/core/tone.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\n\n/**\n * Customer tonality (domino D8 / F2): per-customer (and global) tone profiles\n * applied automatically to generated communication. Customer profile fields\n * override the global default; the merged profile is rendered into a tone\n * instruction for the LLM (used by draft_email / sequences / journeys).\n */\nexport interface ToneProfile {\n formality?: string; // e.g. formal | casual | friendly\n language?: string; // e.g. de | en\n dos?: string[];\n donts?: string[];\n examples?: string[];\n}\n\nfunction globalPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"tone.json\");\n}\nfunction customerPath(dataDir: string, slug: string): string {\n return path.join(dataDir, \"customers\", slug, \"tone.json\");\n}\n\nfunction readProfile(p: string): ToneProfile {\n if (!fs.existsSync(p)) return {};\n try {\n return JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as ToneProfile;\n } catch {\n return {};\n }\n}\n\nexport function setTone(dataDir: string, profile: ToneProfile, slug?: string): void {\n const p = slug ? customerPath(dataDir, slug) : globalPath(dataDir);\n const merged = { ...readProfile(p), ...profile };\n fs.mkdirSync(path.dirname(p), { recursive: true });\n fs.writeFileSync(p, JSON.stringify(merged, null, 2), \"utf-8\");\n}\n\n/** Effective profile: global as base, customer fields override. */\nexport function resolveTone(dataDir: string, slug?: string): ToneProfile {\n const global = readProfile(globalPath(dataDir));\n if (!slug) return global;\n return { ...global, ...readProfile(customerPath(dataDir, slug)) };\n}\n\n/** Render a tone profile into an instruction string (\"\" when blank). */\nexport function toneInstruction(profile: ToneProfile): string {\n const parts: string[] = [];\n if (profile.formality) parts.push(`tone: ${profile.formality}`);\n if (profile.language) parts.push(`language: ${profile.language}`);\n if (profile.dos && profile.dos.length) parts.push(`prefer: ${profile.dos.join(\", \")}`);\n if (profile.donts && profile.donts.length) parts.push(`avoid: ${profile.donts.join(\", \")}`);\n return parts.join(\"; \");\n}\n"],"mappings":";;;;;;AAiBA,SAAS,WAAW,SAAyB;CAC3C,OAAO,KAAA,QAAK,KAAK,SAAS,YAAY,WAAW;AACnD;AACA,SAAS,aAAa,SAAiB,MAAsB;CAC3D,OAAO,KAAA,QAAK,KAAK,SAAS,aAAa,MAAM,WAAW;AAC1D;AAEA,SAAS,YAAY,GAAwB;CAC3C,IAAI,CAAC,GAAA,QAAG,WAAW,CAAC,GAAG,OAAO,CAAC;CAC/B,IAAI;EACF,OAAO,KAAK,MAAM,GAAA,QAAG,aAAa,GAAG,OAAO,CAAW;CACzD,QAAQ;EACN,OAAO,CAAC;CACV;AACF;;AAUA,SAAgB,YAAY,SAAiB,MAA4B;CACvE,MAAM,SAAS,YAAY,WAAW,OAAO,CAAC;CAC9C,IAAI,CAAC,MAAM,OAAO;CAClB,OAAO;EAAE,GAAG;EAAQ,GAAG,YAAY,aAAa,SAAS,IAAI,CAAC;CAAE;AAClE;;AAGA,SAAgB,gBAAgB,SAA8B;CAC5D,MAAM,QAAkB,CAAC;CACzB,IAAI,QAAQ,WAAW,MAAM,KAAK,SAAS,QAAQ,WAAW;CAC9D,IAAI,QAAQ,UAAU,MAAM,KAAK,aAAa,QAAQ,UAAU;CAChE,IAAI,QAAQ,OAAO,QAAQ,IAAI,QAAQ,MAAM,KAAK,WAAW,QAAQ,IAAI,KAAK,IAAI,GAAG;CACrF,IAAI,QAAQ,SAAS,QAAQ,MAAM,QAAQ,MAAM,KAAK,UAAU,QAAQ,MAAM,KAAK,IAAI,GAAG;CAC1F,OAAO,MAAM,KAAK,IAAI;AACxB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"tone-vNb2DAAD.js","names":[],"sources":["../src/core/tone.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\n\n/**\n * Customer tonality (domino D8 / F2): per-customer (and global) tone profiles\n * applied automatically to generated communication. Customer profile fields\n * override the global default; the merged profile is rendered into a tone\n * instruction for the LLM (used by draft_email / sequences / journeys).\n */\nexport interface ToneProfile {\n formality?: string; // e.g. formal | casual | friendly\n language?: string; // e.g. de | en\n dos?: string[];\n donts?: string[];\n examples?: string[];\n}\n\nfunction globalPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"tone.json\");\n}\nfunction customerPath(dataDir: string, slug: string): string {\n return path.join(dataDir, \"customers\", slug, \"tone.json\");\n}\n\nfunction readProfile(p: string): ToneProfile {\n if (!fs.existsSync(p)) return {};\n try {\n return JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as ToneProfile;\n } catch {\n return {};\n }\n}\n\nexport function setTone(dataDir: string, profile: ToneProfile, slug?: string): void {\n const p = slug ? customerPath(dataDir, slug) : globalPath(dataDir);\n const merged = { ...readProfile(p), ...profile };\n fs.mkdirSync(path.dirname(p), { recursive: true });\n fs.writeFileSync(p, JSON.stringify(merged, null, 2), \"utf-8\");\n}\n\n/** Effective profile: global as base, customer fields override. */\nexport function resolveTone(dataDir: string, slug?: string): ToneProfile {\n const global = readProfile(globalPath(dataDir));\n if (!slug) return global;\n return { ...global, ...readProfile(customerPath(dataDir, slug)) };\n}\n\n/** Render a tone profile into an instruction string (\"\" when blank). */\nexport function toneInstruction(profile: ToneProfile): string {\n const parts: string[] = [];\n if (profile.formality) parts.push(`tone: ${profile.formality}`);\n if (profile.language) parts.push(`language: ${profile.language}`);\n if (profile.dos && profile.dos.length) parts.push(`prefer: ${profile.dos.join(\", \")}`);\n if (profile.donts && profile.donts.length) parts.push(`avoid: ${profile.donts.join(\", \")}`);\n return parts.join(\"; \");\n}\n"],"mappings":";;;AAiBA,SAAS,WAAW,SAAyB;CAC3C,OAAO,KAAK,KAAK,SAAS,YAAY,WAAW;AACnD;AACA,SAAS,aAAa,SAAiB,MAAsB;CAC3D,OAAO,KAAK,KAAK,SAAS,aAAa,MAAM,WAAW;AAC1D;AAEA,SAAS,YAAY,GAAwB;CAC3C,IAAI,CAAC,GAAG,WAAW,CAAC,GAAG,OAAO,CAAC;CAC/B,IAAI;EACF,OAAO,KAAK,MAAM,GAAG,aAAa,GAAG,OAAO,CAAW;CACzD,QAAQ;EACN,OAAO,CAAC;CACV;AACF;;AAUA,SAAgB,YAAY,SAAiB,MAA4B;CACvE,MAAM,SAAS,YAAY,WAAW,OAAO,CAAC;CAC9C,IAAI,CAAC,MAAM,OAAO;CAClB,OAAO;EAAE,GAAG;EAAQ,GAAG,YAAY,aAAa,SAAS,IAAI,CAAC;CAAE;AAClE;;AAGA,SAAgB,gBAAgB,SAA8B;CAC5D,MAAM,QAAkB,CAAC;CACzB,IAAI,QAAQ,WAAW,MAAM,KAAK,SAAS,QAAQ,WAAW;CAC9D,IAAI,QAAQ,UAAU,MAAM,KAAK,aAAa,QAAQ,UAAU;CAChE,IAAI,QAAQ,OAAO,QAAQ,IAAI,QAAQ,MAAM,KAAK,WAAW,QAAQ,IAAI,KAAK,IAAI,GAAG;CACrF,IAAI,QAAQ,SAAS,QAAQ,MAAM,QAAQ,MAAM,KAAK,UAAU,QAAQ,MAAM,KAAK,IAAI,GAAG;CAC1F,OAAO,MAAM,KAAK,IAAI;AACxB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"transcript-watcher-CL2QUygI.js","names":[],"sources":["../src/sync/transcript-watcher.ts"],"sourcesContent":["// src/sync/transcript-watcher.ts\n// chokidar v4 — NO glob support in watch(), use ignored as a function\nimport chokidar, { type FSWatcher } from \"chokidar\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport matter from \"gray-matter\";\n\ninterface WatchOptions {\n paths: string[];\n extensions: string[];\n dataDir: string;\n onFile: (filePath: string) => Promise<void>;\n}\n\nexport function watchTranscripts(opts: WatchOptions): FSWatcher {\n const { paths, extensions, onFile } = opts;\n\n const watcher = chokidar.watch(paths, {\n // v4: ignored is a function (no glob strings)\n ignored: (p: string, stats?: fs.Stats) => {\n if (stats?.isDirectory()) return false;\n return !extensions.some((ext) => p.endsWith(ext));\n },\n awaitWriteFinish: { stabilityThreshold: 2000, pollInterval: 100 },\n ignoreInitial: false,\n persistent: true,\n });\n\n watcher.on(\"add\", (filePath) => {\n onFile(filePath).catch((err: unknown) => {\n console.error(`[transcript-watcher] Error processing ${filePath}:`, (err as Error).message);\n });\n });\n\n return watcher;\n}\n\nexport async function processTranscriptFile(\n filePath: string,\n slug: string,\n dataDir: string\n): Promise<void> {\n const source = `file://${filePath}`;\n\n const { readInteractions, appendInteraction } = await import(\"../fs/interactions-writer.js\");\n const existing = await readInteractions(dataDir, slug);\n if (existing.includes(source)) return;\n\n const content = fs.readFileSync(filePath, \"utf-8\");\n const date = new Date().toISOString().slice(0, 10);\n const filename = filePath.split(\"/\").pop() ?? filePath;\n\n await appendInteraction(dataDir, slug, {\n date,\n type: \"Meeting\",\n with: filename,\n subject: filename,\n summary: content.slice(0, 500) + (content.length > 500 ? \"...\" : \"\"),\n nextSteps: [],\n sourceRef: source,\n synced: new Date().toISOString(),\n });\n\n const { indexInLanceDB } = await import(\"../core/lancedb.js\");\n await indexInLanceDB(dataDir, slug, content.slice(0, 2000), source, {\n date,\n type: \"Meeting\",\n }).catch((err: unknown) => {\n process.stderr.write(`[transcript-watcher] LanceDB index failed: ${(err as Error).message}\\n`);\n });\n}\n\nfunction readCustomerName(customersDir: string, slug: string): string {\n const mainFactsPath = path.join(customersDir, slug, \"main_facts.md\");\n if (!fs.existsSync(mainFactsPath)) return slug;\n try {\n const raw = matter(fs.readFileSync(mainFactsPath, \"utf-8\"));\n return typeof raw.data[\"name\"] === \"string\" ? raw.data[\"name\"] : slug;\n } catch {\n return slug;\n }\n}\n\nfunction fuzzyMatchCustomer(\n filePath: string,\n content: string,\n candidates: Array<{ slug: string; name: string }>\n): { slug: string } | null {\n const filename = path.basename(filePath).toLowerCase();\n const contentPreview = content.toLowerCase().slice(0, 5_000);\n\n let best: { slug: string; score: number } | null = null;\n\n for (const { slug, name } of candidates) {\n let score = 0;\n const nameLower = name.toLowerCase();\n const slugLower = slug.toLowerCase();\n\n // Filename match is the strongest signal\n if (filename.includes(slugLower) || filename.includes(nameLower.replace(/\\s+/g, \"-\"))) {\n score += 10;\n }\n\n // Count name occurrences in content\n const escaped = nameLower.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n score += contentPreview.match(new RegExp(escaped, \"g\"))?.length ?? 0;\n\n if (score > 0 && (!best || score > best.score)) {\n best = { slug, score };\n }\n }\n\n return best ? { slug: best.slug } : null;\n}\n\nexport async function processTranscriptFileAutoMatch(\n filePath: string,\n dataDir: string\n): Promise<void> {\n const customersDir = path.join(dataDir, \"customers\");\n if (!fs.existsSync(customersDir)) {\n await recordUnmatched(dataDir, filePath, \"no_customers_defined\");\n return;\n }\n\n const slugs = fs.readdirSync(customersDir).filter((s) => {\n try {\n return fs.statSync(path.join(customersDir, s)).isDirectory();\n } catch {\n return false;\n }\n });\n\n if (slugs.length === 0) {\n await recordUnmatched(dataDir, filePath, \"no_customers_defined\");\n return;\n }\n\n const content = fs.readFileSync(filePath, \"utf-8\");\n const candidates = slugs.map((slug) => ({\n slug,\n name: readCustomerName(customersDir, slug),\n }));\n\n const matchedSlug = await matchCustomer(filePath, content, candidates);\n\n if (matchedSlug) {\n await processTranscriptFile(filePath, matchedSlug, dataDir);\n } else {\n await recordUnmatched(dataDir, filePath, \"no_customer_match\");\n }\n}\n\n/**\n * Resolve a transcript to a customer slug. Prefers LLM recognition (when an\n * ANTHROPIC_API_KEY is configured) and falls back to the filename/content\n * heuristic. The LLM result is only trusted when it names a known candidate\n * with at least medium confidence — guarding against hallucinated slugs.\n */\nasync function matchCustomer(\n filePath: string,\n content: string,\n candidates: Array<{ slug: string; name: string }>\n): Promise<string | null> {\n try {\n const { recognizeCustomer } = await import(\"../core/llm.js\");\n const llm = await recognizeCustomer(content, candidates);\n if (llm.slug && llm.confidence !== \"low\" && candidates.some((c) => c.slug === llm.slug)) {\n return llm.slug;\n }\n } catch (err: unknown) {\n process.stderr.write(\n `[transcript-watcher] LLM recognition failed, using heuristic: ${(err as Error).message}\\n`\n );\n }\n\n return fuzzyMatchCustomer(filePath, content, candidates)?.slug ?? null;\n}\n\nasync function recordUnmatched(\n dataDir: string,\n filePath: string,\n reason: \"no_customer_match\" | \"no_customers_defined\"\n): Promise<void> {\n const { appendUnmatched } = await import(\"../fs/unmatched-transcripts.js\");\n appendUnmatched(dataDir, { filePath, addedAt: new Date().toISOString(), reason });\n process.stderr.write(`[transcript-watcher] Unmatched: ${filePath} (${reason})\\n`);\n}\n"],"mappings":"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"}
@@ -1,26 +0,0 @@
1
- import path from "path";
2
- import fs from "fs";
3
- //#region src/fs/unmatched-transcripts.ts
4
- function getUnmatchedPath(dataDir) {
5
- return path.join(dataDir, ".agentic", "unmatched-transcripts.json");
6
- }
7
- function readUnmatched(dataDir) {
8
- const filePath = getUnmatchedPath(dataDir);
9
- if (!fs.existsSync(filePath)) return [];
10
- try {
11
- return JSON.parse(fs.readFileSync(filePath, "utf-8"));
12
- } catch {
13
- return [];
14
- }
15
- }
16
- function appendUnmatched(dataDir, entry) {
17
- const filePath = getUnmatchedPath(dataDir);
18
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
19
- const existing = readUnmatched(dataDir);
20
- existing.push(entry);
21
- fs.writeFileSync(filePath, JSON.stringify(existing, null, 2), "utf-8");
22
- }
23
- //#endregion
24
- export { readUnmatched as n, appendUnmatched as t };
25
-
26
- //# sourceMappingURL=unmatched-transcripts-BsH5bhkU.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"unmatched-transcripts-BsH5bhkU.js","names":[],"sources":["../src/fs/unmatched-transcripts.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\n\nexport interface UnmatchedTranscript {\n filePath: string;\n addedAt: string; // ISO timestamp\n reason: \"no_customer_match\" | \"no_customers_defined\";\n}\n\nfunction getUnmatchedPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"unmatched-transcripts.json\");\n}\n\nexport function readUnmatched(dataDir: string): UnmatchedTranscript[] {\n const filePath = getUnmatchedPath(dataDir);\n if (!fs.existsSync(filePath)) return [];\n try {\n return JSON.parse(fs.readFileSync(filePath, \"utf-8\") as string) as UnmatchedTranscript[];\n } catch {\n return [];\n }\n}\n\nexport function appendUnmatched(dataDir: string, entry: UnmatchedTranscript): void {\n const filePath = getUnmatchedPath(dataDir);\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n const existing = readUnmatched(dataDir);\n existing.push(entry);\n fs.writeFileSync(filePath, JSON.stringify(existing, null, 2), \"utf-8\");\n}\n\nexport function clearUnmatched(dataDir: string): void {\n const filePath = getUnmatchedPath(dataDir);\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, \"[]\", \"utf-8\");\n}\n"],"mappings":";;;AASA,SAAS,iBAAiB,SAAyB;CACjD,OAAO,KAAK,KAAK,SAAS,YAAY,4BAA4B;AACpE;AAEA,SAAgB,cAAc,SAAwC;CACpE,MAAM,WAAW,iBAAiB,OAAO;CACzC,IAAI,CAAC,GAAG,WAAW,QAAQ,GAAG,OAAO,CAAC;CACtC,IAAI;EACF,OAAO,KAAK,MAAM,GAAG,aAAa,UAAU,OAAO,CAAW;CAChE,QAAQ;EACN,OAAO,CAAC;CACV;AACF;AAEA,SAAgB,gBAAgB,SAAiB,OAAkC;CACjF,MAAM,WAAW,iBAAiB,OAAO;CACzC,GAAG,UAAU,KAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;CACxD,MAAM,WAAW,cAAc,OAAO;CACtC,SAAS,KAAK,KAAK;CACnB,GAAG,cAAc,UAAU,KAAK,UAAU,UAAU,MAAM,CAAC,GAAG,OAAO;AACvE"}
@@ -1,2 +0,0 @@
1
- import { t as appendUnmatched } from "./unmatched-transcripts-BsH5bhkU.js";
2
- export { appendUnmatched };
@@ -1,2 +0,0 @@
1
- import { t as handleUpdateDeal } from "./update-deal-DKC79skb.js";
2
- export { handleUpdateDeal };
@@ -1 +0,0 @@
1
- {"version":3,"file":"usage-D0-TYJkw.js","names":[],"sources":["../src/core/usage.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\n\n/**\n * LLM token-cost observability (domino D3 / F7). Every LLM call can record its\n * token usage + computed cost, attributed per customer/tool, into an append-only\n * NDJSON ledger. Basis for cost transparency and outcome/consumption pricing.\n */\nexport interface UsageEntry {\n timestamp: string;\n slug?: string;\n tool?: string;\n model: string;\n inputTokens: number;\n outputTokens: number;\n costUsd: number;\n}\n\nexport interface PricePerMillion {\n input: number;\n output: number;\n}\n\n// USD per 1M tokens. Override via .agentic/llm-pricing.json. Defaults are\n// conservative Haiku-class estimates — verify against current provider pricing.\nconst DEFAULT_PRICING: Record<string, PricePerMillion> = {\n \"claude-haiku-4-5\": { input: 1.0, output: 5.0 },\n default: { input: 1.0, output: 5.0 },\n};\n\nfunction ledgerPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"usage.ndjson\");\n}\n\nfunction loadPricing(dataDir: string): Record<string, PricePerMillion> {\n const p = path.join(dataDir, \".agentic\", \"llm-pricing.json\");\n if (!fs.existsSync(p)) return DEFAULT_PRICING;\n try {\n const custom = JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as Record<\n string,\n PricePerMillion\n >;\n return { ...DEFAULT_PRICING, ...custom };\n } catch {\n return DEFAULT_PRICING;\n }\n}\n\nexport function computeCost(\n pricing: Record<string, PricePerMillion>,\n model: string,\n inputTokens: number,\n outputTokens: number\n): number {\n const price = pricing[model] ?? pricing[\"default\"]!;\n const cost = (inputTokens / 1_000_000) * price.input + (outputTokens / 1_000_000) * price.output;\n return Math.round(cost * 1_000_000) / 1_000_000;\n}\n\nexport function recordUsage(\n dataDir: string,\n u: { slug?: string; tool?: string; model: string; inputTokens: number; outputTokens: number }\n): void {\n const entry: UsageEntry = {\n timestamp: new Date().toISOString(),\n ...(u.slug ? { slug: u.slug } : {}),\n ...(u.tool ? { tool: u.tool } : {}),\n model: u.model,\n inputTokens: u.inputTokens,\n outputTokens: u.outputTokens,\n costUsd: computeCost(loadPricing(dataDir), u.model, u.inputTokens, u.outputTokens),\n };\n const p = ledgerPath(dataDir);\n try {\n fs.mkdirSync(path.dirname(p), { recursive: true });\n fs.appendFileSync(p, JSON.stringify(entry) + \"\\n\", \"utf-8\");\n } catch {\n /* non-fatal: usage logging must never break an LLM call */\n }\n}\n\nexport function loadUsage(dataDir: string): UsageEntry[] {\n const p = ledgerPath(dataDir);\n if (!fs.existsSync(p)) return [];\n try {\n return (fs.readFileSync(p, \"utf-8\") as string)\n .split(\"\\n\")\n .filter(Boolean)\n .map((line) => JSON.parse(line) as UsageEntry);\n } catch {\n return [];\n }\n}\n\nexport interface UsageAggregate {\n totalInputTokens: number;\n totalOutputTokens: number;\n totalCostUsd: number;\n calls: number;\n bySlug: Record<\n string,\n { inputTokens: number; outputTokens: number; costUsd: number; calls: number }\n >;\n}\n\nexport function aggregateUsage(dataDir: string, opts: { slug?: string } = {}): UsageAggregate {\n const agg: UsageAggregate = {\n totalInputTokens: 0,\n totalOutputTokens: 0,\n totalCostUsd: 0,\n calls: 0,\n bySlug: {},\n };\n for (const e of loadUsage(dataDir)) {\n if (opts.slug && e.slug !== opts.slug) continue;\n agg.totalInputTokens += e.inputTokens;\n agg.totalOutputTokens += e.outputTokens;\n agg.totalCostUsd += e.costUsd;\n agg.calls++;\n const key = e.slug ?? \"(unattributed)\";\n const b = agg.bySlug[key] ?? { inputTokens: 0, outputTokens: 0, costUsd: 0, calls: 0 };\n b.inputTokens += e.inputTokens;\n b.outputTokens += e.outputTokens;\n b.costUsd += e.costUsd;\n b.calls++;\n agg.bySlug[key] = b;\n }\n agg.totalCostUsd = Math.round(agg.totalCostUsd * 1_000_000) / 1_000_000;\n return agg;\n}\n"],"mappings":";;;AAyBA,MAAM,kBAAmD;CACvD,oBAAoB;EAAE,OAAO;EAAK,QAAQ;CAAI;CAC9C,SAAS;EAAE,OAAO;EAAK,QAAQ;CAAI;AACrC;AAEA,SAAS,WAAW,SAAyB;CAC3C,OAAO,KAAK,KAAK,SAAS,YAAY,cAAc;AACtD;AAEA,SAAS,YAAY,SAAkD;CACrE,MAAM,IAAI,KAAK,KAAK,SAAS,YAAY,kBAAkB;CAC3D,IAAI,CAAC,GAAG,WAAW,CAAC,GAAG,OAAO;CAC9B,IAAI;EACF,MAAM,SAAS,KAAK,MAAM,GAAG,aAAa,GAAG,OAAO,CAAW;EAI/D,OAAO;GAAE,GAAG;GAAiB,GAAG;EAAO;CACzC,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAgB,YACd,SACA,OACA,aACA,cACQ;CACR,MAAM,QAAQ,QAAQ,UAAU,QAAQ;CACxC,MAAM,OAAQ,cAAc,MAAa,MAAM,QAAS,eAAe,MAAa,MAAM;CAC1F,OAAO,KAAK,MAAM,OAAO,GAAS,IAAI;AACxC;AAEA,SAAgB,YACd,SACA,GACM;CACN,MAAM,QAAoB;EACxB,4BAAW,IAAI,KAAK,GAAE,YAAY;EAClC,GAAI,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;EACjC,GAAI,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;EACjC,OAAO,EAAE;EACT,aAAa,EAAE;EACf,cAAc,EAAE;EAChB,SAAS,YAAY,YAAY,OAAO,GAAG,EAAE,OAAO,EAAE,aAAa,EAAE,YAAY;CACnF;CACA,MAAM,IAAI,WAAW,OAAO;CAC5B,IAAI;EACF,GAAG,UAAU,KAAK,QAAQ,CAAC,GAAG,EAAE,WAAW,KAAK,CAAC;EACjD,GAAG,eAAe,GAAG,KAAK,UAAU,KAAK,IAAI,MAAM,OAAO;CAC5D,QAAQ,CAER;AACF;AAEA,SAAgB,UAAU,SAA+B;CACvD,MAAM,IAAI,WAAW,OAAO;CAC5B,IAAI,CAAC,GAAG,WAAW,CAAC,GAAG,OAAO,CAAC;CAC/B,IAAI;EACF,OAAQ,GAAG,aAAa,GAAG,OAAO,EAC/B,MAAM,IAAI,EACV,OAAO,OAAO,EACd,KAAK,SAAS,KAAK,MAAM,IAAI,CAAe;CACjD,QAAQ;EACN,OAAO,CAAC;CACV;AACF;AAaA,SAAgB,eAAe,SAAiB,OAA0B,CAAC,GAAmB;CAC5F,MAAM,MAAsB;EAC1B,kBAAkB;EAClB,mBAAmB;EACnB,cAAc;EACd,OAAO;EACP,QAAQ,CAAC;CACX;CACA,KAAK,MAAM,KAAK,UAAU,OAAO,GAAG;EAClC,IAAI,KAAK,QAAQ,EAAE,SAAS,KAAK,MAAM;EACvC,IAAI,oBAAoB,EAAE;EAC1B,IAAI,qBAAqB,EAAE;EAC3B,IAAI,gBAAgB,EAAE;EACtB,IAAI;EACJ,MAAM,MAAM,EAAE,QAAQ;EACtB,MAAM,IAAI,IAAI,OAAO,QAAQ;GAAE,aAAa;GAAG,cAAc;GAAG,SAAS;GAAG,OAAO;EAAE;EACrF,EAAE,eAAe,EAAE;EACnB,EAAE,gBAAgB,EAAE;EACpB,EAAE,WAAW,EAAE;EACf,EAAE;EACF,IAAI,OAAO,OAAO;CACpB;CACA,IAAI,eAAe,KAAK,MAAM,IAAI,eAAe,GAAS,IAAI;CAC9D,OAAO;AACT"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"vault-DXCg29W-.js","names":[],"sources":["../src/core/encryption.ts","../src/core/vault.ts"],"sourcesContent":["import crypto from \"crypto\";\n\nconst ALGORITHM = \"aes-256-gcm\";\nconst KEY_LEN = 32;\nconst IV_LEN = 12;\n\nexport function deriveKey(secret: string): Buffer {\n return crypto.scryptSync(secret, \"dxcrm-salt-v1\", KEY_LEN);\n}\n\nexport interface EncryptedField {\n iv: string;\n ciphertext: string;\n authTag: string;\n}\n\nexport function encryptField(plaintext: string, key: Buffer): EncryptedField {\n const iv = crypto.randomBytes(IV_LEN);\n const cipher = crypto.createCipheriv(ALGORITHM, key, iv);\n const ciphertext = Buffer.concat([cipher.update(plaintext, \"utf8\"), cipher.final()]);\n const authTag = cipher.getAuthTag();\n return {\n iv: iv.toString(\"hex\"),\n ciphertext: ciphertext.toString(\"hex\"),\n authTag: authTag.toString(\"hex\"),\n };\n}\n\nexport function decryptField(encrypted: EncryptedField, key: Buffer): string {\n const iv = Buffer.from(encrypted.iv, \"hex\");\n const ciphertext = Buffer.from(encrypted.ciphertext, \"hex\");\n const authTag = Buffer.from(encrypted.authTag, \"hex\");\n const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);\n decipher.setAuthTag(authTag);\n return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString(\"utf8\");\n}\n\nexport function encryptFieldStr(plaintext: string, secret: string): string {\n return JSON.stringify(encryptField(plaintext, deriveKey(secret)));\n}\n\nexport function decryptFieldStr(encryptedJson: string, secret: string): string {\n return decryptField(JSON.parse(encryptedJson) as EncryptedField, deriveKey(secret));\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport { encryptFieldStr, decryptFieldStr } from \"./encryption.js\";\n\n/**\n * Local credential vault (domino D12 / F6): a dependency-free, AES-256-GCM\n * encrypted store for secrets the agent must hold (portal passwords, API keys)\n * but the customer markdown must never contain in plaintext. The whole vault is\n * a single encrypted blob at `.agentic/vault.enc`; the master key lives only in\n * the operator's environment (DXCRM_VAULT_KEY) — never on disk, never committed.\n * A GUI is a documented follow-up; this is the headless, scriptable core.\n */\ntype VaultData = Record<string, string>;\n\nfunction vaultPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"vault.enc\");\n}\n\n/** Read + decrypt the vault. Empty vault when the file does not exist yet. */\nexport function loadVault(dataDir: string, key: string): VaultData {\n const p = vaultPath(dataDir);\n if (!fs.existsSync(p)) return {};\n const encrypted = fs.readFileSync(p, \"utf-8\") as string;\n let decrypted: string;\n try {\n decrypted = decryptFieldStr(encrypted, key);\n } catch {\n // AES-GCM auth-tag failure → wrong master key (or a corrupted/tampered file).\n throw new Error(\"Unable to decrypt vault: wrong master key or corrupted vault file.\");\n }\n const data = JSON.parse(decrypted) as VaultData;\n return data && typeof data === \"object\" ? data : {};\n}\n\n/** Encrypt + write the vault atomically (overwrites the single blob). */\nexport function saveVault(dataDir: string, key: string, data: VaultData): void {\n const p = vaultPath(dataDir);\n fs.mkdirSync(path.dirname(p), { recursive: true });\n fs.writeFileSync(p, encryptFieldStr(JSON.stringify(data), key), \"utf-8\");\n}\n\n/** Store (or overwrite) a secret under `name`. */\nexport function setSecret(dataDir: string, key: string, name: string, value: string): void {\n const data = loadVault(dataDir, key);\n data[name] = value;\n saveVault(dataDir, key, data);\n}\n\n/** Retrieve a secret; returns undefined when absent. Throws on wrong master key. */\nexport function getSecret(dataDir: string, key: string, name: string): string | undefined {\n return loadVault(dataDir, key)[name];\n}\n\n/** List the names of all stored secrets (values stay encrypted). */\nexport function listSecretKeys(dataDir: string, key: string): string[] {\n return Object.keys(loadVault(dataDir, key));\n}\n\n/** Remove a secret; returns true when something was deleted. */\nexport function removeSecret(dataDir: string, key: string, name: string): boolean {\n const data = loadVault(dataDir, key);\n if (!(name in data)) return false;\n delete data[name];\n saveVault(dataDir, key, data);\n return true;\n}\n"],"mappings":";;;;AAEA,MAAM,YAAY;AAClB,MAAM,UAAU;AAChB,MAAM,SAAS;AAEf,SAAgB,UAAU,QAAwB;CAChD,OAAO,OAAO,WAAW,QAAQ,iBAAiB,OAAO;AAC3D;AAQA,SAAgB,aAAa,WAAmB,KAA6B;CAC3E,MAAM,KAAK,OAAO,YAAY,MAAM;CACpC,MAAM,SAAS,OAAO,eAAe,WAAW,KAAK,EAAE;CACvD,MAAM,aAAa,OAAO,OAAO,CAAC,OAAO,OAAO,WAAW,MAAM,GAAG,OAAO,MAAM,CAAC,CAAC;CACnF,MAAM,UAAU,OAAO,WAAW;CAClC,OAAO;EACL,IAAI,GAAG,SAAS,KAAK;EACrB,YAAY,WAAW,SAAS,KAAK;EACrC,SAAS,QAAQ,SAAS,KAAK;CACjC;AACF;AAEA,SAAgB,aAAa,WAA2B,KAAqB;CAC3E,MAAM,KAAK,OAAO,KAAK,UAAU,IAAI,KAAK;CAC1C,MAAM,aAAa,OAAO,KAAK,UAAU,YAAY,KAAK;CAC1D,MAAM,UAAU,OAAO,KAAK,UAAU,SAAS,KAAK;CACpD,MAAM,WAAW,OAAO,iBAAiB,WAAW,KAAK,EAAE;CAC3D,SAAS,WAAW,OAAO;CAC3B,OAAO,OAAO,OAAO,CAAC,SAAS,OAAO,UAAU,GAAG,SAAS,MAAM,CAAC,CAAC,EAAE,SAAS,MAAM;AACvF;AAEA,SAAgB,gBAAgB,WAAmB,QAAwB;CACzE,OAAO,KAAK,UAAU,aAAa,WAAW,UAAU,MAAM,CAAC,CAAC;AAClE;AAEA,SAAgB,gBAAgB,eAAuB,QAAwB;CAC7E,OAAO,aAAa,KAAK,MAAM,aAAa,GAAqB,UAAU,MAAM,CAAC;AACpF;;;AC7BA,SAAS,UAAU,SAAyB;CAC1C,OAAO,KAAK,KAAK,SAAS,YAAY,WAAW;AACnD;;AAGA,SAAgB,UAAU,SAAiB,KAAwB;CACjE,MAAM,IAAI,UAAU,OAAO;CAC3B,IAAI,CAAC,GAAG,WAAW,CAAC,GAAG,OAAO,CAAC;CAC/B,MAAM,YAAY,GAAG,aAAa,GAAG,OAAO;CAC5C,IAAI;CACJ,IAAI;EACF,YAAY,gBAAgB,WAAW,GAAG;CAC5C,QAAQ;EAEN,MAAM,IAAI,MAAM,oEAAoE;CACtF;CACA,MAAM,OAAO,KAAK,MAAM,SAAS;CACjC,OAAO,QAAQ,OAAO,SAAS,WAAW,OAAO,CAAC;AACpD;;AAGA,SAAgB,UAAU,SAAiB,KAAa,MAAuB;CAC7E,MAAM,IAAI,UAAU,OAAO;CAC3B,GAAG,UAAU,KAAK,QAAQ,CAAC,GAAG,EAAE,WAAW,KAAK,CAAC;CACjD,GAAG,cAAc,GAAG,gBAAgB,KAAK,UAAU,IAAI,GAAG,GAAG,GAAG,OAAO;AACzE;;AAGA,SAAgB,UAAU,SAAiB,KAAa,MAAc,OAAqB;CACzF,MAAM,OAAO,UAAU,SAAS,GAAG;CACnC,KAAK,QAAQ;CACb,UAAU,SAAS,KAAK,IAAI;AAC9B;;AAGA,SAAgB,UAAU,SAAiB,KAAa,MAAkC;CACxF,OAAO,UAAU,SAAS,GAAG,EAAE;AACjC;;AAGA,SAAgB,eAAe,SAAiB,KAAuB;CACrE,OAAO,OAAO,KAAK,UAAU,SAAS,GAAG,CAAC;AAC5C;;AAGA,SAAgB,aAAa,SAAiB,KAAa,MAAuB;CAChF,MAAM,OAAO,UAAU,SAAS,GAAG;CACnC,IAAI,EAAE,QAAQ,OAAO,OAAO;CAC5B,OAAO,KAAK;CACZ,UAAU,SAAS,KAAK,IAAI;CAC5B,OAAO;AACT"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"webhooks-7EpA05Qr.js","names":[],"sources":["../src/core/webhooks.ts"],"sourcesContent":["import { createHash, createHmac, randomBytes } from \"crypto\";\nimport fs from \"fs\";\nimport path from \"path\";\n\n/**\n * Outbound webhooks (event-driven architecture, N5-2). Subscriptions live in\n * .agentic/webhooks.json; failed deliveries are queued in\n * .agentic/webhook-failures.json (replay store) and re-attempted by\n * retryFailures (e.g. from the daemon) — backoff via periodic replay.\n */\nexport interface WebhookSubscription {\n id: string;\n url: string;\n events: string[];\n secret?: string;\n createdAt: string;\n}\n\nexport interface WebhookFailure {\n id: string;\n subscriptionId: string;\n url: string;\n secret?: string;\n event: string;\n payload: unknown;\n attempts: number;\n lastError: string;\n queuedAt: string;\n}\n\nfunction subsPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"webhooks.json\");\n}\nfunction failuresPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"webhook-failures.json\");\n}\n\nfunction readJson<T>(p: string, key: string): T[] {\n if (!fs.existsSync(p)) return [];\n try {\n const data = JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as Record<string, T[]>;\n return Array.isArray(data[key]) ? data[key]! : [];\n } catch {\n return [];\n }\n}\nfunction writeJson<T>(p: string, key: string, items: T[]): void {\n fs.mkdirSync(path.dirname(p), { recursive: true });\n fs.writeFileSync(p, JSON.stringify({ [key]: items }, null, 2), \"utf-8\");\n}\n\nexport function loadWebhooks(dataDir: string): WebhookSubscription[] {\n return readJson<WebhookSubscription>(subsPath(dataDir), \"subscriptions\");\n}\n\nexport function addWebhook(\n dataDir: string,\n url: string,\n events: string[],\n secret?: string\n): WebhookSubscription {\n const sub: WebhookSubscription = {\n id: `wh_${randomBytes(5).toString(\"hex\")}`,\n url,\n events,\n ...(secret ? { secret } : {}),\n createdAt: new Date().toISOString(),\n };\n writeJson(subsPath(dataDir), \"subscriptions\", [...loadWebhooks(dataDir), sub]);\n return sub;\n}\n\nexport function removeWebhook(dataDir: string, id: string): boolean {\n const subs = loadWebhooks(dataDir);\n const next = subs.filter((s) => s.id !== id);\n if (next.length === subs.length) return false;\n writeJson(subsPath(dataDir), \"subscriptions\", next);\n return true;\n}\n\n/** A subscription matches an event by exact name, \"*\", or a \"prefix.*\" pattern. */\nexport function matchSubscriptions(\n subs: WebhookSubscription[],\n event: string\n): WebhookSubscription[] {\n return subs.filter((s) =>\n s.events.some((pat) => {\n if (pat === \"*\" || pat === event) return true;\n if (pat.endsWith(\".*\")) return event.startsWith(pat.slice(0, -1));\n return false;\n })\n );\n}\n\nexport function signPayload(secret: string, body: string): string {\n return createHmac(\"sha256\", secret).update(body).digest(\"hex\");\n}\n\nexport function loadFailures(dataDir: string): WebhookFailure[] {\n return readJson<WebhookFailure>(failuresPath(dataDir), \"failures\");\n}\n\nasync function deliver(\n sub: WebhookSubscription,\n event: string,\n payload: unknown\n): Promise<{ ok: boolean; error?: string }> {\n const body = JSON.stringify({ event, payload, deliveredAt: new Date().toISOString() });\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"X-DXCRM-Event\": event,\n };\n if (sub.secret) headers[\"X-DXCRM-Signature\"] = `sha256=${signPayload(sub.secret, body)}`;\n try {\n const res = (await fetch(sub.url, { method: \"POST\", headers, body })) as {\n ok: boolean;\n status: number;\n };\n if (!res.ok) return { ok: false, error: `HTTP ${res.status}` };\n return { ok: true };\n } catch (err) {\n return { ok: false, error: (err as Error).message };\n }\n}\n\n/** Emit an event to all matching subscriptions; queue failures for replay. */\nexport async function emitEvent(dataDir: string, event: string, payload: unknown): Promise<void> {\n const matched = matchSubscriptions(loadWebhooks(dataDir), event);\n if (matched.length === 0) return;\n const failures = loadFailures(dataDir);\n for (const sub of matched) {\n const r = await deliver(sub, event, payload);\n if (!r.ok) {\n failures.push({\n id: `whf_${createHash(\"sha256\").update(`${sub.id}:${event}:${Date.now()}`).digest(\"hex\").slice(0, 10)}`,\n subscriptionId: sub.id,\n url: sub.url,\n ...(sub.secret ? { secret: sub.secret } : {}),\n event,\n payload,\n attempts: 1,\n lastError: r.error ?? \"unknown\",\n queuedAt: new Date().toISOString(),\n });\n }\n }\n if (failures.length > 0) writeJson(failuresPath(dataDir), \"failures\", failures);\n}\n\n/** Re-attempt queued failures; remove on success, increment attempts on failure. */\nexport async function retryFailures(\n dataDir: string\n): Promise<{ retried: number; stillFailing: number }> {\n const failures = loadFailures(dataDir);\n const remaining: WebhookFailure[] = [];\n let retried = 0;\n for (const f of failures) {\n const sub: WebhookSubscription = {\n id: f.subscriptionId,\n url: f.url,\n events: [f.event],\n ...(f.secret ? { secret: f.secret } : {}),\n createdAt: f.queuedAt,\n };\n const r = await deliver(sub, f.event, f.payload);\n if (r.ok) retried++;\n else remaining.push({ ...f, attempts: f.attempts + 1, lastError: r.error ?? \"unknown\" });\n }\n writeJson(failuresPath(dataDir), \"failures\", remaining);\n return { retried, stillFailing: remaining.length };\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"webhooks-BO2UAnmn.js","names":[],"sources":["../src/core/webhooks.ts"],"sourcesContent":["import { createHash, createHmac, randomBytes } from \"crypto\";\nimport fs from \"fs\";\nimport path from \"path\";\n\n/**\n * Outbound webhooks (event-driven architecture, N5-2). Subscriptions live in\n * .agentic/webhooks.json; failed deliveries are queued in\n * .agentic/webhook-failures.json (replay store) and re-attempted by\n * retryFailures (e.g. from the daemon) — backoff via periodic replay.\n */\nexport interface WebhookSubscription {\n id: string;\n url: string;\n events: string[];\n secret?: string;\n createdAt: string;\n}\n\nexport interface WebhookFailure {\n id: string;\n subscriptionId: string;\n url: string;\n secret?: string;\n event: string;\n payload: unknown;\n attempts: number;\n lastError: string;\n queuedAt: string;\n}\n\nfunction subsPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"webhooks.json\");\n}\nfunction failuresPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"webhook-failures.json\");\n}\n\nfunction readJson<T>(p: string, key: string): T[] {\n if (!fs.existsSync(p)) return [];\n try {\n const data = JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as Record<string, T[]>;\n return Array.isArray(data[key]) ? data[key]! : [];\n } catch {\n return [];\n }\n}\nfunction writeJson<T>(p: string, key: string, items: T[]): void {\n fs.mkdirSync(path.dirname(p), { recursive: true });\n fs.writeFileSync(p, JSON.stringify({ [key]: items }, null, 2), \"utf-8\");\n}\n\nexport function loadWebhooks(dataDir: string): WebhookSubscription[] {\n return readJson<WebhookSubscription>(subsPath(dataDir), \"subscriptions\");\n}\n\nexport function addWebhook(\n dataDir: string,\n url: string,\n events: string[],\n secret?: string\n): WebhookSubscription {\n const sub: WebhookSubscription = {\n id: `wh_${randomBytes(5).toString(\"hex\")}`,\n url,\n events,\n ...(secret ? { secret } : {}),\n createdAt: new Date().toISOString(),\n };\n writeJson(subsPath(dataDir), \"subscriptions\", [...loadWebhooks(dataDir), sub]);\n return sub;\n}\n\nexport function removeWebhook(dataDir: string, id: string): boolean {\n const subs = loadWebhooks(dataDir);\n const next = subs.filter((s) => s.id !== id);\n if (next.length === subs.length) return false;\n writeJson(subsPath(dataDir), \"subscriptions\", next);\n return true;\n}\n\n/** A subscription matches an event by exact name, \"*\", or a \"prefix.*\" pattern. */\nexport function matchSubscriptions(\n subs: WebhookSubscription[],\n event: string\n): WebhookSubscription[] {\n return subs.filter((s) =>\n s.events.some((pat) => {\n if (pat === \"*\" || pat === event) return true;\n if (pat.endsWith(\".*\")) return event.startsWith(pat.slice(0, -1));\n return false;\n })\n );\n}\n\nexport function signPayload(secret: string, body: string): string {\n return createHmac(\"sha256\", secret).update(body).digest(\"hex\");\n}\n\nexport function loadFailures(dataDir: string): WebhookFailure[] {\n return readJson<WebhookFailure>(failuresPath(dataDir), \"failures\");\n}\n\nasync function deliver(\n sub: WebhookSubscription,\n event: string,\n payload: unknown\n): Promise<{ ok: boolean; error?: string }> {\n const body = JSON.stringify({ event, payload, deliveredAt: new Date().toISOString() });\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"X-DXCRM-Event\": event,\n };\n if (sub.secret) headers[\"X-DXCRM-Signature\"] = `sha256=${signPayload(sub.secret, body)}`;\n try {\n const res = (await fetch(sub.url, { method: \"POST\", headers, body })) as {\n ok: boolean;\n status: number;\n };\n if (!res.ok) return { ok: false, error: `HTTP ${res.status}` };\n return { ok: true };\n } catch (err) {\n return { ok: false, error: (err as Error).message };\n }\n}\n\n/** Emit an event to all matching subscriptions; queue failures for replay. */\nexport async function emitEvent(dataDir: string, event: string, payload: unknown): Promise<void> {\n const matched = matchSubscriptions(loadWebhooks(dataDir), event);\n if (matched.length === 0) return;\n const failures = loadFailures(dataDir);\n for (const sub of matched) {\n const r = await deliver(sub, event, payload);\n if (!r.ok) {\n failures.push({\n id: `whf_${createHash(\"sha256\").update(`${sub.id}:${event}:${Date.now()}`).digest(\"hex\").slice(0, 10)}`,\n subscriptionId: sub.id,\n url: sub.url,\n ...(sub.secret ? { secret: sub.secret } : {}),\n event,\n payload,\n attempts: 1,\n lastError: r.error ?? \"unknown\",\n queuedAt: new Date().toISOString(),\n });\n }\n }\n if (failures.length > 0) writeJson(failuresPath(dataDir), \"failures\", failures);\n}\n\n/** Re-attempt queued failures; remove on success, increment attempts on failure. */\nexport async function retryFailures(\n dataDir: string\n): Promise<{ retried: number; stillFailing: number }> {\n const failures = loadFailures(dataDir);\n const remaining: WebhookFailure[] = [];\n let retried = 0;\n for (const f of failures) {\n const sub: WebhookSubscription = {\n id: f.subscriptionId,\n url: f.url,\n events: [f.event],\n ...(f.secret ? { secret: f.secret } : {}),\n createdAt: f.queuedAt,\n };\n const r = await deliver(sub, f.event, f.payload);\n if (r.ok) retried++;\n else remaining.push({ ...f, attempts: f.attempts + 1, lastError: r.error ?? \"unknown\" });\n }\n writeJson(failuresPath(dataDir), \"failures\", remaining);\n return { retried, stillFailing: remaining.length };\n}\n"],"mappings":";;;;AA8BA,SAAS,SAAS,SAAyB;CACzC,OAAO,KAAK,KAAK,SAAS,YAAY,eAAe;AACvD;AACA,SAAS,aAAa,SAAyB;CAC7C,OAAO,KAAK,KAAK,SAAS,YAAY,uBAAuB;AAC/D;AAEA,SAAS,SAAY,GAAW,KAAkB;CAChD,IAAI,CAAC,GAAG,WAAW,CAAC,GAAG,OAAO,CAAC;CAC/B,IAAI;EACF,MAAM,OAAO,KAAK,MAAM,GAAG,aAAa,GAAG,OAAO,CAAW;EAC7D,OAAO,MAAM,QAAQ,KAAK,IAAI,IAAI,KAAK,OAAQ,CAAC;CAClD,QAAQ;EACN,OAAO,CAAC;CACV;AACF;AACA,SAAS,UAAa,GAAW,KAAa,OAAkB;CAC9D,GAAG,UAAU,KAAK,QAAQ,CAAC,GAAG,EAAE,WAAW,KAAK,CAAC;CACjD,GAAG,cAAc,GAAG,KAAK,UAAU,GAAG,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,OAAO;AACxE;AAEA,SAAgB,aAAa,SAAwC;CACnE,OAAO,SAA8B,SAAS,OAAO,GAAG,eAAe;AACzE;;AA4BA,SAAgB,mBACd,MACA,OACuB;CACvB,OAAO,KAAK,QAAQ,MAClB,EAAE,OAAO,MAAM,QAAQ;EACrB,IAAI,QAAQ,OAAO,QAAQ,OAAO,OAAO;EACzC,IAAI,IAAI,SAAS,IAAI,GAAG,OAAO,MAAM,WAAW,IAAI,MAAM,GAAG,EAAE,CAAC;EAChE,OAAO;CACT,CAAC,CACH;AACF;AAEA,SAAgB,YAAY,QAAgB,MAAsB;CAChE,OAAO,WAAW,UAAU,MAAM,EAAE,OAAO,IAAI,EAAE,OAAO,KAAK;AAC/D;AAEA,SAAgB,aAAa,SAAmC;CAC9D,OAAO,SAAyB,aAAa,OAAO,GAAG,UAAU;AACnE;AAEA,eAAe,QACb,KACA,OACA,SAC0C;CAC1C,MAAM,OAAO,KAAK,UAAU;EAAE;EAAO;EAAS,8BAAa,IAAI,KAAK,GAAE,YAAY;CAAE,CAAC;CACrF,MAAM,UAAkC;EACtC,gBAAgB;EAChB,iBAAiB;CACnB;CACA,IAAI,IAAI,QAAQ,QAAQ,uBAAuB,UAAU,YAAY,IAAI,QAAQ,IAAI;CACrF,IAAI;EACF,MAAM,MAAO,MAAM,MAAM,IAAI,KAAK;GAAE,QAAQ;GAAQ;GAAS;EAAK,CAAC;EAInE,IAAI,CAAC,IAAI,IAAI,OAAO;GAAE,IAAI;GAAO,OAAO,QAAQ,IAAI;EAAS;EAC7D,OAAO,EAAE,IAAI,KAAK;CACpB,SAAS,KAAK;EACZ,OAAO;GAAE,IAAI;GAAO,OAAQ,IAAc;EAAQ;CACpD;AACF;;AAGA,eAAsB,UAAU,SAAiB,OAAe,SAAiC;CAC/F,MAAM,UAAU,mBAAmB,aAAa,OAAO,GAAG,KAAK;CAC/D,IAAI,QAAQ,WAAW,GAAG;CAC1B,MAAM,WAAW,aAAa,OAAO;CACrC,KAAK,MAAM,OAAO,SAAS;EACzB,MAAM,IAAI,MAAM,QAAQ,KAAK,OAAO,OAAO;EAC3C,IAAI,CAAC,EAAE,IACL,SAAS,KAAK;GACZ,IAAI,OAAO,WAAW,QAAQ,EAAE,OAAO,GAAG,IAAI,GAAG,GAAG,MAAM,GAAG,KAAK,IAAI,GAAG,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;GACpG,gBAAgB,IAAI;GACpB,KAAK,IAAI;GACT,GAAI,IAAI,SAAS,EAAE,QAAQ,IAAI,OAAO,IAAI,CAAC;GAC3C;GACA;GACA,UAAU;GACV,WAAW,EAAE,SAAS;GACtB,2BAAU,IAAI,KAAK,GAAE,YAAY;EACnC,CAAC;CAEL;CACA,IAAI,SAAS,SAAS,GAAG,UAAU,aAAa,OAAO,GAAG,YAAY,QAAQ;AAChF"}