@datasynx/agentic-crm 0.1.0 → 1.0.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 (286) hide show
  1. package/README.md +264 -670
  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-D8iYqDAr.js} +6 -6
  5. package/dist/{ask-CID3jnuL.js.map → ask-D8iYqDAr.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/auth-B5DcjJ_6.js +2 -0
  11. package/dist/{auth-DFWwWcYD.js → auth-DDXZTwS0.js} +4 -13
  12. package/dist/auth-DDXZTwS0.js.map +1 -0
  13. package/dist/{autofill-Di_-SP7t.js → autofill-B9VtlR2j.js} +2 -2
  14. package/dist/{autofill-Di_-SP7t.js.map → autofill-B9VtlR2j.js.map} +1 -1
  15. package/dist/{backup-CeMk9z86.js → backup-CTlIxUdO.js} +5 -7
  16. package/dist/backup-CTlIxUdO.js.map +1 -0
  17. package/dist/backup-LFnC09oV.js +2 -0
  18. package/dist/{churn-C28IgnAj.js → churn-DN9WDGNM.js} +3 -3
  19. package/dist/{churn-C28IgnAj.js.map → churn-DN9WDGNM.js.map} +1 -1
  20. package/dist/cli.js +282 -184
  21. package/dist/cli.js.map +1 -1
  22. package/dist/{compliance-CKSBoQUe.js → compliance-Bc12Hn9a.js} +3 -3
  23. package/dist/{compliance-CKSBoQUe.js.map → compliance-Bc12Hn9a.js.map} +1 -1
  24. package/dist/{compliance-CujOqAKk.js → compliance-TqYQXhBj.js} +1 -1
  25. package/dist/{compliance-B1kk5-YS.js → compliance-kq0xHRw3.js} +3 -3
  26. package/dist/{compliance-B1kk5-YS.js.map → compliance-kq0xHRw3.js.map} +1 -1
  27. package/dist/{compliance-B91zNvCR.cjs → compliance-pAj9FcGI.cjs} +3 -3
  28. package/dist/{compliance-B91zNvCR.cjs.map → compliance-pAj9FcGI.cjs.map} +1 -1
  29. package/dist/{context-builder-BzWAp3Zs.js → context-builder-7Uab5-G4.js} +3 -2
  30. package/dist/context-builder-7Uab5-G4.js.map +1 -0
  31. package/dist/context-builder-hmOPvgso.js +2 -0
  32. package/dist/{custom-fields-CzNeD3_v.js → custom-fields-BMyz5Ruh.js} +1 -1
  33. package/dist/{custom-fields-Pl2t9xzp.js → custom-fields-GzpOHW_2.js} +4 -13
  34. package/dist/custom-fields-GzpOHW_2.js.map +1 -0
  35. package/dist/{custom-objects-CIFrmQ2V.js → custom-objects-BNy-ayR-.js} +1 -1
  36. package/dist/{custom-objects-BHgn1GEX.js → custom-objects-CxW1gHwJ.js} +10 -25
  37. package/dist/custom-objects-CxW1gHwJ.js.map +1 -0
  38. package/dist/{customer-dir-DIylZ8Q6.js → customer-dir-CkMMXhb0.js} +9 -4
  39. package/dist/customer-dir-CkMMXhb0.js.map +1 -0
  40. package/dist/daemon/worker.js +66 -40
  41. package/dist/daemon/worker.js.map +1 -1
  42. package/dist/doctor-C14-vnJ1.js +103 -0
  43. package/dist/doctor-C14-vnJ1.js.map +1 -0
  44. package/dist/{enrichment-3XvgGDfB.js → enrichment-CDFdWmvD.js} +3 -3
  45. package/dist/{enrichment-3XvgGDfB.js.map → enrichment-CDFdWmvD.js.map} +1 -1
  46. package/dist/{file-lock-B_zi7NQl.js → file-lock-CcHotQkZ.js} +3 -4
  47. package/dist/file-lock-CcHotQkZ.js.map +1 -0
  48. package/dist/{gmail-sync-rQaVqKWd.js → gmail-sync-C-NmibzS.js} +13 -9
  49. package/dist/gmail-sync-C-NmibzS.js.map +1 -0
  50. package/dist/{gmail-sync-DIaxInDT.js → gmail-sync-DueE6tl5.js} +14 -10
  51. package/dist/gmail-sync-DueE6tl5.js.map +1 -0
  52. package/dist/{gmail-sync-hHm9gaWd.cjs → gmail-sync-GEy3oVvw.cjs} +13 -9
  53. package/dist/gmail-sync-GEy3oVvw.cjs.map +1 -0
  54. package/dist/{gmail-webhook-handler-DS7OlRPX.js → gmail-webhook-handler-B26COilD.js} +2 -2
  55. package/dist/{gmail-webhook-handler-e5Od25FX.js → gmail-webhook-handler-kGKpbY9h.js} +4 -4
  56. package/dist/{gmail-webhook-handler-e5Od25FX.js.map → gmail-webhook-handler-kGKpbY9h.js.map} +1 -1
  57. package/dist/{goal-engine-KpBftn4V.js → goal-engine-BbroPhqm.js} +10 -11
  58. package/dist/goal-engine-BbroPhqm.js.map +1 -0
  59. package/dist/{goal-engine-CUZSpERI.js → goal-engine-CfDAJTFt.js} +1 -1
  60. package/dist/{google-drive-sync-DEPcqFca.js → google-drive-sync-D1n7WKZn.js} +3 -3
  61. package/dist/{google-drive-sync-DEPcqFca.js.map → google-drive-sync-D1n7WKZn.js.map} +1 -1
  62. package/dist/{hygiene-DZqfYpFf.js → hygiene-DzQPnc6P.js} +3 -3
  63. package/dist/{hygiene-DZqfYpFf.js.map → hygiene-DzQPnc6P.js.map} +1 -1
  64. package/dist/identity-CB7j-Zr1.js +2 -0
  65. package/dist/{identity-CI6olMNm.js → identity-_uZ3Lbr2.js} +2 -2
  66. package/dist/{identity-CI6olMNm.js.map → identity-_uZ3Lbr2.js.map} +1 -1
  67. package/dist/{import-hubspot-BaK71U_K.js → import-hubspot-DB4n89jy.js} +51 -45
  68. package/dist/import-hubspot-DB4n89jy.js.map +1 -0
  69. package/dist/{index-V8BFaH-b.d.ts → index-B0IMMrp_.d.ts} +8 -4
  70. package/dist/{index-V8BFaH-b.d.ts.map → index-B0IMMrp_.d.ts.map} +1 -1
  71. package/dist/{index-YqwMd6aQ.d.cts → index-pY7tYXwH.d.cts} +8 -4
  72. package/dist/{index-YqwMd6aQ.d.cts.map → index-pY7tYXwH.d.cts.map} +1 -1
  73. package/dist/index.cjs +19 -21
  74. package/dist/index.cjs.map +1 -1
  75. package/dist/index.d.cts +8 -4
  76. package/dist/index.d.cts.map +1 -1
  77. package/dist/index.d.ts +8 -4
  78. package/dist/index.d.ts.map +1 -1
  79. package/dist/index.js +19 -21
  80. package/dist/index.js.map +1 -1
  81. package/dist/{interactions-writer-DO3KcSR3.js → interactions-writer-BZzUIgJd.js} +5 -2
  82. package/dist/interactions-writer-BZzUIgJd.js.map +1 -0
  83. package/dist/{interactions-writer-SLHnoEeE.js → interactions-writer-DbSyI2rt.js} +32 -3
  84. package/dist/interactions-writer-DbSyI2rt.js.map +1 -0
  85. package/dist/interactions-writer-RJB8SWf2.js +2 -0
  86. package/dist/{interactions-writer-CrPStUll.cjs → interactions-writer-a2yzBd7T.cjs} +5 -2
  87. package/dist/interactions-writer-a2yzBd7T.cjs.map +1 -0
  88. package/dist/json-store-WWsFzXub.js +43 -0
  89. package/dist/json-store-WWsFzXub.js.map +1 -0
  90. package/dist/{knowledge-base-D0Fh40kc.js → knowledge-base-DHNc4hVj.js} +43 -16
  91. package/dist/knowledge-base-DHNc4hVj.js.map +1 -0
  92. package/dist/{lancedb-CCBbpulq.js → lancedb-CswQEE5K.js} +1 -1
  93. package/dist/{lancedb-rlvWoPwl.js → lancedb-CuHKNsNZ.js} +4 -3
  94. package/dist/lancedb-CuHKNsNZ.js.map +1 -0
  95. package/dist/{lead-model-BCFzyktm.js → lead-model-CEmx7te7.js} +6 -14
  96. package/dist/lead-model-CEmx7te7.js.map +1 -0
  97. package/dist/{llm-Z8RIYkpF.js → llm-BnSUBisu.js} +2 -2
  98. package/dist/{llm-Z8RIYkpF.js.map → llm-BnSUBisu.js.map} +1 -1
  99. package/dist/{llm-iijeXmgq.cjs → llm-CXycmEl9.cjs} +2 -2
  100. package/dist/{llm-iijeXmgq.cjs.map → llm-CXycmEl9.cjs.map} +1 -1
  101. package/dist/{llm-DEjWcqmW.js → llm-DSX1-wFu.js} +1 -1
  102. package/dist/{llm-DvzZqva0.js → llm-PZzgPphl.js} +3 -3
  103. package/dist/{llm-DvzZqva0.js.map → llm-PZzgPphl.js.map} +1 -1
  104. package/dist/logger-BkInaGoV.cjs +167 -0
  105. package/dist/logger-BkInaGoV.cjs.map +1 -0
  106. package/dist/logger-Dyl4VcLO.js +147 -0
  107. package/dist/logger-Dyl4VcLO.js.map +1 -0
  108. package/dist/logger-UaF5p9d1.js +147 -0
  109. package/dist/logger-UaF5p9d1.js.map +1 -0
  110. package/dist/logger-vKQS34w9.js +2 -0
  111. package/dist/mcp-CdTJWTJf.d.cts.map +1 -1
  112. package/dist/mcp-CdTJWTJf.d.ts.map +1 -1
  113. package/dist/mcp.cjs +327 -303
  114. package/dist/mcp.cjs.map +1 -1
  115. package/dist/mcp.d.cts.map +1 -1
  116. package/dist/mcp.d.ts.map +1 -1
  117. package/dist/mcp.js +327 -303
  118. package/dist/mcp.js.map +1 -1
  119. package/dist/{memory-Cy6-Tbyl.js → memory-D8hmgD9d.js} +1 -1
  120. package/dist/{memory-Bb6ky3kb.js → memory-Dzr9dXSM.js} +4 -11
  121. package/dist/memory-Dzr9dXSM.js.map +1 -0
  122. package/dist/{microsoft-calendar-B6MMtUQK.js → microsoft-calendar-jIu9K5zX.js} +4 -4
  123. package/dist/{microsoft-calendar-B6MMtUQK.js.map → microsoft-calendar-jIu9K5zX.js.map} +1 -1
  124. package/dist/{microsoft-sync-CpZVoSuq.js → microsoft-sync-R_r8HL-B.js} +5 -5
  125. package/dist/{microsoft-sync-CpZVoSuq.js.map → microsoft-sync-R_r8HL-B.js.map} +1 -1
  126. package/dist/{nba-3wanmJ0U.js → nba-mTJ4yEqD.js} +3 -3
  127. package/dist/{nba-3wanmJ0U.js.map → nba-mTJ4yEqD.js.map} +1 -1
  128. package/dist/{notification-dispatcher-0vYNngWe.js → notification-dispatcher-inpKyuBz.js} +7 -3
  129. package/dist/notification-dispatcher-inpKyuBz.js.map +1 -0
  130. package/dist/{pipeline-writer-BqBrYrQc.js → pipeline-writer-0LJ6Qkat.js} +1 -1
  131. package/dist/{pipeline-writer-N2omexxp.cjs → pipeline-writer-B1tRAhuD.cjs} +11 -3
  132. package/dist/pipeline-writer-B1tRAhuD.cjs.map +1 -0
  133. package/dist/{pipeline-writer-BvVquKIe.js → pipeline-writer-CIllfnZl.js} +5 -3
  134. package/dist/pipeline-writer-CIllfnZl.js.map +1 -0
  135. package/dist/{pipeline-writer-eufx_0o1.js → pipeline-writer-rDj-ni6q.js} +6 -4
  136. package/dist/pipeline-writer-rDj-ni6q.js.map +1 -0
  137. package/dist/{proactive-agent-BgQXw3ac.js → proactive-agent-B7u3Bj_l.js} +6 -6
  138. package/dist/{proactive-agent-BgQXw3ac.js.map → proactive-agent-B7u3Bj_l.js.map} +1 -1
  139. package/dist/{proactive-worker-BrLHNhjH.js → proactive-worker-1zkm6aJD.js} +7 -8
  140. package/dist/proactive-worker-1zkm6aJD.js.map +1 -0
  141. package/dist/{push-manager-CowY-0IK.js → push-manager-BXM-IHfP.js} +1 -1
  142. package/dist/{push-manager-CdqIIkuh.js → push-manager-C0ECQgva.js} +4 -4
  143. package/dist/push-manager-C0ECQgva.js.map +1 -0
  144. package/dist/{quote-generator-OhSFsi3x.js → quote-generator-ByUyIYtw.js} +1 -1
  145. package/dist/{quote-generator-BfwENXzg.js → quote-generator-CTdR8eEI.js} +5 -5
  146. package/dist/quote-generator-CTdR8eEI.js.map +1 -0
  147. package/dist/rbac-DzbyFhVH.js +2 -0
  148. package/dist/{rbac-CTIktZaC.js → rbac-msmBc_tK.js} +19 -12
  149. package/dist/rbac-msmBc_tK.js.map +1 -0
  150. package/dist/regex-Jt5DatPi.js +13 -0
  151. package/dist/regex-Jt5DatPi.js.map +1 -0
  152. package/dist/{relationship-health-odxEoQdJ.js → relationship-health-ZZNXR1RZ.js} +8 -16
  153. package/dist/relationship-health-ZZNXR1RZ.js.map +1 -0
  154. package/dist/{revenue-simulation-Bqf2DLVB.js → revenue-simulation-D8f_YkUY.js} +9 -19
  155. package/dist/revenue-simulation-D8f_YkUY.js.map +1 -0
  156. package/dist/{revenue-simulation-BJdRTEHc.js → revenue-simulation-njJZlTqm.js} +1 -1
  157. package/dist/safe-path-mpp0dKtO.js +18 -0
  158. package/dist/safe-path-mpp0dKtO.js.map +1 -0
  159. package/dist/{segments-BqcD5HIl.js → segments-DI3LOQNe.js} +5 -14
  160. package/dist/segments-DI3LOQNe.js.map +1 -0
  161. package/dist/sequence-engine-C6nnewHX.js +2 -0
  162. package/dist/{sequence-engine-J1lTW_in.js → sequence-engine-DNTVLq7o.js} +15 -8
  163. package/dist/sequence-engine-DNTVLq7o.js.map +1 -0
  164. package/dist/{sequence-store-DaaWr0Os.js → sequence-store-CmYb6s0g.js} +6 -5
  165. package/dist/sequence-store-CmYb6s0g.js.map +1 -0
  166. package/dist/{server-Dyva03K8.js → server-DqSMYhSA.js} +278 -220
  167. package/dist/server-DqSMYhSA.js.map +1 -0
  168. package/dist/{session-D9ub6Wl1.js → session-B6XaP83h.js} +3 -3
  169. package/dist/session-B6XaP83h.js.map +1 -0
  170. package/dist/{session-B9AilxOE.js → session-BgGDyP2C.js} +3 -3
  171. package/dist/session-BgGDyP2C.js.map +1 -0
  172. package/dist/session-Bp4zTh4l.js +2 -0
  173. package/dist/{session-D0qFkBla.cjs → session-Mm7GQbSH.cjs} +3 -3
  174. package/dist/session-Mm7GQbSH.cjs.map +1 -0
  175. package/dist/{session-store-C8tEvMPw.js → session-store-DWxJ5Pof.js} +79 -17
  176. package/dist/session-store-DWxJ5Pof.js.map +1 -0
  177. package/dist/{session-store-B0QZE8Bx.cjs → session-store-yfwnj0OC.cjs} +126 -16
  178. package/dist/session-store-yfwnj0OC.cjs.map +1 -0
  179. package/dist/{sla-engine-5IhTsBUR.js → sla-engine-CP2KiKDS.js} +1 -1
  180. package/dist/{sla-engine-BqX-7u-7.js → sla-engine-O-A1ntu_.js} +2 -2
  181. package/dist/{sla-engine-BqX-7u-7.js.map → sla-engine-O-A1ntu_.js.map} +1 -1
  182. package/dist/{sop-Vp0UPWFW.js → sop-BV7ICAFR.js} +4 -11
  183. package/dist/sop-BV7ICAFR.js.map +1 -0
  184. package/dist/{sop-DkhVChGy.js → sop-D33qTHUb.js} +1 -1
  185. package/dist/survey-engine-DKctGcLQ.js +2 -0
  186. package/dist/{survey-engine-DBjCYqCv.js → survey-engine-DngXBv47.js} +5 -4
  187. package/dist/survey-engine-DngXBv47.js.map +1 -0
  188. package/dist/{sync-state-CwLSt_1m.js → sync-state-BaA8LbTI.js} +1 -1
  189. package/dist/{sync-state-ChaLbamC.js → sync-state-DMZgzpez.js} +4 -12
  190. package/dist/sync-state-DMZgzpez.js.map +1 -0
  191. package/dist/{ticket-writer-CjqKeIRD.js → ticket-writer-DsfpeLGZ.js} +1 -1
  192. package/dist/{ticket-writer-j2oX_Wal.js → ticket-writer-a9on36Wb.js} +12 -24
  193. package/dist/ticket-writer-a9on36Wb.js.map +1 -0
  194. package/dist/{tone-Bdm5uaht.js → tone-C7bqK69y.js} +5 -12
  195. package/dist/tone-C7bqK69y.js.map +1 -0
  196. package/dist/{tone-DRKlZgPr.cjs → tone-Cmc7O2Fx.cjs} +3 -9
  197. package/dist/tone-Cmc7O2Fx.cjs.map +1 -0
  198. package/dist/{tone-vNb2DAAD.js → tone-mXSftvTn.js} +3 -8
  199. package/dist/tone-mXSftvTn.js.map +1 -0
  200. package/dist/{transcript-watcher-CL2QUygI.js → transcript-watcher-0mh2ZhmH.js} +18 -11
  201. package/dist/transcript-watcher-0mh2ZhmH.js.map +1 -0
  202. package/dist/unmatched-transcripts-C92zAoM4.js +2 -0
  203. package/dist/unmatched-transcripts-DC-VQ9YS.js +16 -0
  204. package/dist/unmatched-transcripts-DC-VQ9YS.js.map +1 -0
  205. package/dist/update-deal-CWy1eLJI.js +2 -0
  206. package/dist/{update-deal-DKC79skb.js → update-deal-DSzr_Aau.js} +3 -3
  207. package/dist/{update-deal-DKC79skb.js.map → update-deal-DSzr_Aau.js.map} +1 -1
  208. package/dist/{usage-D0-TYJkw.js → usage-BVlFlKW_.js} +8 -6
  209. package/dist/usage-BVlFlKW_.js.map +1 -0
  210. package/dist/usage-CClTf5e6.cjs.map +1 -1
  211. package/dist/usage-D0u9a-lV.js.map +1 -1
  212. package/dist/{vault-DXCg29W-.js → vault-CfwZdNzC.js} +3 -4
  213. package/dist/vault-CfwZdNzC.js.map +1 -0
  214. package/dist/{vault-C1D3zScD.js → vault-DxKP4_R2.js} +1 -1
  215. package/dist/{webhooks-Xn6zO6kd.cjs → webhooks-CwW-3kvG.cjs} +5 -19
  216. package/dist/webhooks-CwW-3kvG.cjs.map +1 -0
  217. package/dist/{webhooks-7EpA05Qr.js → webhooks-DXr1IoKn.js} +8 -21
  218. package/dist/webhooks-DXr1IoKn.js.map +1 -0
  219. package/dist/{webhooks-BO2UAnmn.js → webhooks-sWZ8CJtR.js} +5 -18
  220. package/dist/webhooks-sWZ8CJtR.js.map +1 -0
  221. package/package.json +11 -2
  222. package/dist/approvals-DpjxGHFp.js.map +0 -1
  223. package/dist/auth-CyFuu9X_.js +0 -2
  224. package/dist/auth-DFWwWcYD.js.map +0 -1
  225. package/dist/backup-CeMk9z86.js.map +0 -1
  226. package/dist/backup-f_hC7rBV.js +0 -2
  227. package/dist/context-builder-BzWAp3Zs.js.map +0 -1
  228. package/dist/context-builder-DlrRcqmJ.js +0 -2
  229. package/dist/custom-fields-Pl2t9xzp.js.map +0 -1
  230. package/dist/custom-objects-BHgn1GEX.js.map +0 -1
  231. package/dist/customer-dir-DIylZ8Q6.js.map +0 -1
  232. package/dist/file-lock-B_zi7NQl.js.map +0 -1
  233. package/dist/gmail-sync-DIaxInDT.js.map +0 -1
  234. package/dist/gmail-sync-hHm9gaWd.cjs.map +0 -1
  235. package/dist/gmail-sync-rQaVqKWd.js.map +0 -1
  236. package/dist/goal-engine-KpBftn4V.js.map +0 -1
  237. package/dist/identity-gyfWdrcX.js +0 -2
  238. package/dist/import-hubspot-BaK71U_K.js.map +0 -1
  239. package/dist/interactions-writer-CrPStUll.cjs.map +0 -1
  240. package/dist/interactions-writer-DO3KcSR3.js.map +0 -1
  241. package/dist/interactions-writer-SLHnoEeE.js.map +0 -1
  242. package/dist/interactions-writer-dSPy1XfO.js +0 -2
  243. package/dist/knowledge-base-D0Fh40kc.js.map +0 -1
  244. package/dist/lancedb-rlvWoPwl.js.map +0 -1
  245. package/dist/lead-model-BCFzyktm.js.map +0 -1
  246. package/dist/memory-Bb6ky3kb.js.map +0 -1
  247. package/dist/notification-dispatcher-0vYNngWe.js.map +0 -1
  248. package/dist/pipeline-writer-BvVquKIe.js.map +0 -1
  249. package/dist/pipeline-writer-N2omexxp.cjs.map +0 -1
  250. package/dist/pipeline-writer-eufx_0o1.js.map +0 -1
  251. package/dist/proactive-worker-BrLHNhjH.js.map +0 -1
  252. package/dist/push-manager-CdqIIkuh.js.map +0 -1
  253. package/dist/quote-generator-BfwENXzg.js.map +0 -1
  254. package/dist/rbac-C7c8tcES.js +0 -2
  255. package/dist/rbac-CTIktZaC.js.map +0 -1
  256. package/dist/relationship-health-odxEoQdJ.js.map +0 -1
  257. package/dist/revenue-simulation-Bqf2DLVB.js.map +0 -1
  258. package/dist/segments-BqcD5HIl.js.map +0 -1
  259. package/dist/sequence-engine-CCTHEBgi.js +0 -2
  260. package/dist/sequence-engine-J1lTW_in.js.map +0 -1
  261. package/dist/sequence-store-DaaWr0Os.js.map +0 -1
  262. package/dist/server-Dyva03K8.js.map +0 -1
  263. package/dist/session-B9AilxOE.js.map +0 -1
  264. package/dist/session-D0qFkBla.cjs.map +0 -1
  265. package/dist/session-D9ub6Wl1.js.map +0 -1
  266. package/dist/session-mWHA71Lw.js +0 -2
  267. package/dist/session-store-B0QZE8Bx.cjs.map +0 -1
  268. package/dist/session-store-C8tEvMPw.js.map +0 -1
  269. package/dist/sop-Vp0UPWFW.js.map +0 -1
  270. package/dist/survey-engine-C06hcQt3.js +0 -2
  271. package/dist/survey-engine-DBjCYqCv.js.map +0 -1
  272. package/dist/sync-state-ChaLbamC.js.map +0 -1
  273. package/dist/ticket-writer-j2oX_Wal.js.map +0 -1
  274. package/dist/tone-Bdm5uaht.js.map +0 -1
  275. package/dist/tone-DRKlZgPr.cjs.map +0 -1
  276. package/dist/tone-vNb2DAAD.js.map +0 -1
  277. package/dist/transcript-watcher-CL2QUygI.js.map +0 -1
  278. package/dist/unmatched-transcripts-BsH5bhkU.js +0 -26
  279. package/dist/unmatched-transcripts-BsH5bhkU.js.map +0 -1
  280. package/dist/unmatched-transcripts-D0PrJ9iz.js +0 -2
  281. package/dist/update-deal-BNwPGaTV.js +0 -2
  282. package/dist/usage-D0-TYJkw.js.map +0 -1
  283. package/dist/vault-DXCg29W-.js.map +0 -1
  284. package/dist/webhooks-7EpA05Qr.js.map +0 -1
  285. package/dist/webhooks-BO2UAnmn.js.map +0 -1
  286. package/dist/webhooks-Xn6zO6kd.cjs.map +0 -1
@@ -1,4 +1,5 @@
1
1
  const require_chunk = require("./chunk-DakpK96I.cjs");
2
+ const require_atomic_write = require("./atomic-write-BYmF-ThH.cjs");
2
3
  let commander = require("commander");
3
4
  let path = require("path");
4
5
  path = require_chunk.__toESM(path, 1);
@@ -8,10 +9,10 @@ let gray_matter = require("gray-matter");
8
9
  gray_matter = require_chunk.__toESM(gray_matter, 1);
9
10
  let zod_validation_error = require("zod-validation-error");
10
11
  let zod = require("zod");
12
+ let crypto = require("crypto");
11
13
  let ansis = require("ansis");
12
14
  ansis = require_chunk.__toESM(ansis, 1);
13
15
  let child_process = require("child_process");
14
- let crypto = require("crypto");
15
16
  //#region src/schemas/main-facts.ts
16
17
  const MainFactsSchema = zod.z.object({
17
18
  name: zod.z.string().min(1),
@@ -34,8 +35,27 @@ const MainFactsSchema = zod.z.object({
34
35
  updated: zod.z.preprocess((v) => v instanceof Date ? v.toISOString().slice(0, 10) : v, zod.z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "YYYY-MM-DD required"))
35
36
  });
36
37
  //#endregion
38
+ //#region src/fs/safe-path.ts
39
+ /**
40
+ * Filesystem path-segment safety. A segment (customer slug, custom-object name,
41
+ * KB article id/category, …) is safe iff it cannot escape its parent directory:
42
+ * no path separators, no `..`, no NUL, not "." or empty, and bounded in length.
43
+ * Enforced wherever an untrusted value (from an MCP tool, API, or import) is used
44
+ * to build a file path, to prevent path-traversal (arbitrary read/write).
45
+ */
46
+ function isSafePathSegment(segment) {
47
+ return typeof segment === "string" && segment.length > 0 && segment.length <= 128 && segment !== "." && !segment.includes("/") && !segment.includes("\\") && !segment.includes("\0") && !segment.includes("..");
48
+ }
49
+ function assertSafePathSegment(segment, kind = "path segment") {
50
+ if (!isSafePathSegment(segment)) throw new Error(`Invalid ${kind}: ${JSON.stringify(segment)}`);
51
+ }
52
+ //#endregion
37
53
  //#region src/fs/customer-dir.ts
54
+ function assertSafeSlug(slug) {
55
+ assertSafePathSegment(slug, "customer slug");
56
+ }
38
57
  function getCustomerDir(dataDir, slug) {
58
+ assertSafeSlug(slug);
39
59
  return path.default.join(dataDir, "customers", slug);
40
60
  }
41
61
  function customerExists(dataDir, slug) {
@@ -62,8 +82,7 @@ async function ensureCustomerDir(dataDir, slug) {
62
82
  async function writeMainFacts(dataDir, slug, facts) {
63
83
  const filePath = path.default.join(getCustomerDir(dataDir, slug), "main_facts.md");
64
84
  const clean = Object.fromEntries(Object.entries(facts).filter(([, v]) => v !== void 0));
65
- const content = gray_matter.default.stringify("", clean);
66
- fs.default.writeFileSync(filePath, content, "utf-8");
85
+ require_atomic_write.writeFileAtomic(filePath, gray_matter.default.stringify("", clean));
67
86
  }
68
87
  async function readMainFacts(dataDir, slug) {
69
88
  const filePath = path.default.join(getCustomerDir(dataDir, slug), "main_facts.md");
@@ -79,6 +98,44 @@ async function readMainFacts(dataDir, slug) {
79
98
  return result.data;
80
99
  }
81
100
  //#endregion
101
+ //#region src/fs/json-store.ts
102
+ /**
103
+ * Small shared JSON persistence helpers. Many modules independently reimplemented
104
+ * the same "read a JSON file, fall back to a default on missing/parse-error" and
105
+ * "write a `{ key: items }` array store" logic — this centralizes both so the
106
+ * behavior (and the silent-fallback semantics) is defined in exactly one place.
107
+ *
108
+ * Writes go through writeFileAtomic, so a crash mid-write can never leave a
109
+ * half-written (corrupt) JSON file — readers always see either the old or the
110
+ * new complete document. This matters for the config, audit, and state files
111
+ * the whole product depends on.
112
+ */
113
+ /** Read and parse a JSON file, returning `fallback` if it is missing or invalid. */
114
+ function readJsonFile(filePath, fallback) {
115
+ if (!fs.default.existsSync(filePath)) return fallback;
116
+ try {
117
+ return JSON.parse(fs.default.readFileSync(filePath, "utf-8"));
118
+ } catch {
119
+ return fallback;
120
+ }
121
+ }
122
+ /** Atomically write `value` as pretty-printed JSON, creating parent dirs as needed. */
123
+ function writeJsonFile(filePath, value) {
124
+ require_atomic_write.writeFileAtomic(filePath, JSON.stringify(value, null, 2));
125
+ }
126
+ /**
127
+ * Read an array stored under `key` in a `{ [key]: T[] }` JSON document. Returns
128
+ * an empty array if the file is missing, unparsable, or the key is not an array.
129
+ */
130
+ function readJsonArray(filePath, key) {
131
+ const arr = readJsonFile(filePath, {})[key];
132
+ return Array.isArray(arr) ? arr : [];
133
+ }
134
+ /** Write an array under `key` as a `{ [key]: items }` JSON document. */
135
+ function writeJsonArray(filePath, key, items) {
136
+ writeJsonFile(filePath, { [key]: items });
137
+ }
138
+ //#endregion
82
139
  //#region src/ui/colors.ts
83
140
  const success = (s) => ansis.default.green(s);
84
141
  const error = (s) => ansis.default.red(s);
@@ -100,9 +157,7 @@ function readAgenticConfig(dataDir) {
100
157
  }
101
158
  }
102
159
  function writeAgenticConfig(dataDir, config) {
103
- const filePath = getConfigPath(dataDir);
104
- fs.default.mkdirSync(path.default.dirname(filePath), { recursive: true });
105
- fs.default.writeFileSync(filePath, JSON.stringify(config, null, 2), "utf-8");
160
+ writeJsonFile(getConfigPath(dataDir), config);
106
161
  }
107
162
  function countDir(dir) {
108
163
  let files = 0;
@@ -184,8 +239,7 @@ function appendBackupLog(dataDir, entry) {
184
239
  entries = entries.filter((e) => e.filename !== entry.filename);
185
240
  entries.unshift(entry);
186
241
  if (entries.length > 100) entries = entries.slice(0, 100);
187
- fs.default.mkdirSync(path.default.dirname(logPath), { recursive: true });
188
- fs.default.writeFileSync(logPath, JSON.stringify(entries, null, 2), "utf-8");
242
+ writeJsonFile(logPath, entries);
189
243
  }
190
244
  function readBackupLog(dataDir) {
191
245
  const logPath = path.default.join(dataDir, ".agentic", "backup-log.json");
@@ -496,13 +550,7 @@ function rbacPath(dataDir) {
496
550
  return path.default.join(dataDir, ".agentic", "rbac.json");
497
551
  }
498
552
  function getRbacConfig(dataDir) {
499
- const p = rbacPath(dataDir);
500
- if (!fs.default.existsSync(p)) return { actors: {} };
501
- try {
502
- return JSON.parse(fs.default.readFileSync(p, "utf-8"));
503
- } catch {
504
- return { actors: {} };
505
- }
553
+ return readJsonFile(rbacPath(dataDir), { actors: {} });
506
554
  }
507
555
  function getRole(dataDir, actor) {
508
556
  const config = getRbacConfig(dataDir);
@@ -530,6 +578,20 @@ function canSeeCustomer(dataDir, actor, slug) {
530
578
  if (!owned) return false;
531
579
  return (owned[actor] ?? []).includes(slug);
532
580
  }
581
+ /**
582
+ * Build a once-loaded predicate for which customers `actor` may see. Equivalent
583
+ * to calling canSeeCustomer per slug, but reads/parses rbac.json a single time
584
+ * (and uses O(1) Set membership) — for hot loops like list_customers.
585
+ */
586
+ function customerVisibility(dataDir, actor) {
587
+ if (!fs.default.existsSync(rbacPath(dataDir))) return () => true;
588
+ if (actor === "system") return () => true;
589
+ const config = getRbacConfig(dataDir);
590
+ const role = config.actors[actor] ?? config.default ?? "rep";
591
+ if (role === "admin" || role === "manager") return () => true;
592
+ const owned = new Set(config.owned_customers?.[actor] ?? []);
593
+ return (slug) => owned.has(slug);
594
+ }
533
595
  //#endregion
534
596
  //#region src/core/session-store.ts
535
597
  let activeSession = null;
@@ -549,6 +611,18 @@ Object.defineProperty(exports, "MainFactsSchema", {
549
611
  return MainFactsSchema;
550
612
  }
551
613
  });
614
+ Object.defineProperty(exports, "assertSafePathSegment", {
615
+ enumerable: true,
616
+ get: function() {
617
+ return assertSafePathSegment;
618
+ }
619
+ });
620
+ Object.defineProperty(exports, "assertSafeSlug", {
621
+ enumerable: true,
622
+ get: function() {
623
+ return assertSafeSlug;
624
+ }
625
+ });
552
626
  Object.defineProperty(exports, "bold", {
553
627
  enumerable: true,
554
628
  get: function() {
@@ -573,6 +647,12 @@ Object.defineProperty(exports, "customerExists", {
573
647
  return customerExists;
574
648
  }
575
649
  });
650
+ Object.defineProperty(exports, "customerVisibility", {
651
+ enumerable: true,
652
+ get: function() {
653
+ return customerVisibility;
654
+ }
655
+ });
576
656
  Object.defineProperty(exports, "enforceRbac", {
577
657
  enumerable: true,
578
658
  get: function() {
@@ -627,6 +707,12 @@ Object.defineProperty(exports, "info", {
627
707
  return info;
628
708
  }
629
709
  });
710
+ Object.defineProperty(exports, "isSafePathSegment", {
711
+ enumerable: true,
712
+ get: function() {
713
+ return isSafePathSegment;
714
+ }
715
+ });
630
716
  Object.defineProperty(exports, "listBackupsInDir", {
631
717
  enumerable: true,
632
718
  get: function() {
@@ -651,6 +737,18 @@ Object.defineProperty(exports, "readBackupLog", {
651
737
  return readBackupLog;
652
738
  }
653
739
  });
740
+ Object.defineProperty(exports, "readJsonArray", {
741
+ enumerable: true,
742
+ get: function() {
743
+ return readJsonArray;
744
+ }
745
+ });
746
+ Object.defineProperty(exports, "readJsonFile", {
747
+ enumerable: true,
748
+ get: function() {
749
+ return readJsonFile;
750
+ }
751
+ });
654
752
  Object.defineProperty(exports, "readMainFacts", {
655
753
  enumerable: true,
656
754
  get: function() {
@@ -687,6 +785,18 @@ Object.defineProperty(exports, "writeAuditEntry", {
687
785
  return writeAuditEntry;
688
786
  }
689
787
  });
788
+ Object.defineProperty(exports, "writeJsonArray", {
789
+ enumerable: true,
790
+ get: function() {
791
+ return writeJsonArray;
792
+ }
793
+ });
794
+ Object.defineProperty(exports, "writeJsonFile", {
795
+ enumerable: true,
796
+ get: function() {
797
+ return writeJsonFile;
798
+ }
799
+ });
690
800
  Object.defineProperty(exports, "writeMainFacts", {
691
801
  enumerable: true,
692
802
  get: function() {
@@ -694,4 +804,4 @@ Object.defineProperty(exports, "writeMainFacts", {
694
804
  }
695
805
  });
696
806
 
697
- //# sourceMappingURL=session-store-B0QZE8Bx.cjs.map
807
+ //# sourceMappingURL=session-store-yfwnj0OC.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-store-yfwnj0OC.cjs","names":["z","matter","Command"],"sources":["../src/schemas/main-facts.ts","../src/fs/safe-path.ts","../src/fs/customer-dir.ts","../src/fs/json-store.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","/**\n * Filesystem path-segment safety. A segment (customer slug, custom-object name,\n * KB article id/category, …) is safe iff it cannot escape its parent directory:\n * no path separators, no `..`, no NUL, not \".\" or empty, and bounded in length.\n * Enforced wherever an untrusted value (from an MCP tool, API, or import) is used\n * to build a file path, to prevent path-traversal (arbitrary read/write).\n */\nexport function isSafePathSegment(segment: unknown): segment is string {\n return (\n typeof segment === \"string\" &&\n segment.length > 0 &&\n segment.length <= 128 &&\n segment !== \".\" &&\n !segment.includes(\"/\") &&\n !segment.includes(\"\\\\\") &&\n !segment.includes(\"\\0\") &&\n !segment.includes(\"..\")\n );\n}\n\nexport function assertSafePathSegment(segment: string, kind = \"path segment\"): void {\n if (!isSafePathSegment(segment)) {\n throw new Error(`Invalid ${kind}: ${JSON.stringify(segment)}`);\n }\n}\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\";\nimport { writeFileAtomic } from \"./atomic-write.js\";\nimport { isSafePathSegment, assertSafePathSegment } from \"./safe-path.js\";\n\n/** A customer slug is safe iff it is a safe filesystem path segment. */\nexport function isSafeSlug(slug: unknown): slug is string {\n return isSafePathSegment(slug);\n}\n\nexport function assertSafeSlug(slug: string): void {\n assertSafePathSegment(slug, \"customer slug\");\n}\n\nexport function getCustomerDir(dataDir: string, slug: string): string {\n assertSafeSlug(slug);\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 writeFileAtomic(filePath, content);\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 fs from \"fs\";\nimport { writeFileAtomic } from \"./atomic-write.js\";\n\n/**\n * Small shared JSON persistence helpers. Many modules independently reimplemented\n * the same \"read a JSON file, fall back to a default on missing/parse-error\" and\n * \"write a `{ key: items }` array store\" logic — this centralizes both so the\n * behavior (and the silent-fallback semantics) is defined in exactly one place.\n *\n * Writes go through writeFileAtomic, so a crash mid-write can never leave a\n * half-written (corrupt) JSON file — readers always see either the old or the\n * new complete document. This matters for the config, audit, and state files\n * the whole product depends on.\n */\n\n/** Read and parse a JSON file, returning `fallback` if it is missing or invalid. */\nexport function readJsonFile<T>(filePath: string, fallback: T): T {\n if (!fs.existsSync(filePath)) return fallback;\n try {\n return JSON.parse(fs.readFileSync(filePath, \"utf-8\") as string) as T;\n } catch {\n return fallback;\n }\n}\n\n/** Atomically write `value` as pretty-printed JSON, creating parent dirs as needed. */\nexport function writeJsonFile(filePath: string, value: unknown): void {\n writeFileAtomic(filePath, JSON.stringify(value, null, 2));\n}\n\n/**\n * Read an array stored under `key` in a `{ [key]: T[] }` JSON document. Returns\n * an empty array if the file is missing, unparsable, or the key is not an array.\n */\nexport function readJsonArray<T>(filePath: string, key: string): T[] {\n const data = readJsonFile<Record<string, unknown>>(filePath, {});\n const arr = data[key];\n return Array.isArray(arr) ? (arr as T[]) : [];\n}\n\n/** Write an array under `key` as a `{ [key]: items }` JSON document. */\nexport function writeJsonArray<T>(filePath: string, key: string, items: T[]): void {\n writeJsonFile(filePath, { [key]: items });\n}\n","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\";\nimport { writeJsonFile } from \"../fs/json-store.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 writeJsonFile(getConfigPath(dataDir), config);\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 writeJsonFile(logPath, entries);\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\";\nimport { readJsonFile, writeJsonFile } from \"../fs/json-store.js\";\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 return readJsonFile<RbacConfig>(rbacPath(dataDir), { actors: {} });\n}\n\nexport function setActorRole(dataDir: string, actor: string, role: Role): void {\n const config = getRbacConfig(dataDir);\n config.actors[actor] = role;\n writeJsonFile(rbacPath(dataDir), config);\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/**\n * Build a once-loaded predicate for which customers `actor` may see. Equivalent\n * to calling canSeeCustomer per slug, but reads/parses rbac.json a single time\n * (and uses O(1) Set membership) — for hot loops like list_customers.\n */\nexport function customerVisibility(dataDir: string, actor: string): (slug: string) => boolean {\n if (!fs.existsSync(rbacPath(dataDir))) return () => true; // open access\n if (actor === \"system\") return () => true;\n const config = getRbacConfig(dataDir);\n const role = config.actors[actor] ?? config.default ?? \"rep\";\n if (role === \"admin\" || role === \"manager\") return () => true;\n const owned = new Set(config.owned_customers?.[actor] ?? []);\n return (slug: string) => owned.has(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":";;;;;;;;;;;;;;;;AAEA,MAAa,kBAAkBA,IAAAA,EAAE,OAAO;CACtC,MAAMA,IAAAA,EAAE,OAAO,EAAE,IAAI,CAAC;CACtB,QAAQA,IAAAA,EAAE,OAAO,EAAE,SAAS;CAC5B,OAAOA,IAAAA,EAAE,OAAO,EAAE,SAAS;CAC3B,OAAOA,IAAAA,EAAE,OAAO,EAAE,SAAS;CAC3B,UAAUA,IAAAA,EAAE,OAAO,EAAE,SAAS;CAC9B,oBAAoBA,IAAAA,EAAE,KAAK;EAAC;EAAY;EAAU;EAAW;CAAQ,CAAC;CACtE,YAAYA,IAAAA,EAAE,OAAO,EAAE,SAAS;CAChC,UAAUA,IAAAA,EAAE,OAAO,EAAE,QAAQ,KAAK;CAClC,iBAAiBA,IAAAA,EAAE,OAAO,EAAE,SAAS;CACrC,UAAUA,IAAAA,EAAE,OAAO,EAAE,SAAS;CAC9B,MAAMA,IAAAA,EAAE,MAAMA,IAAAA,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;CACpC,SAASA,IAAAA,EAAE,YACR,MAAO,aAAa,OAAO,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE,IAAI,GAC3DA,IAAAA,EAAE,OAAO,EAAE,MAAM,uBAAuB,qBAAqB,CAC/D;CACA,SAASA,IAAAA,EAAE,YACR,MAAO,aAAa,OAAO,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE,IAAI,GAC3DA,IAAAA,EAAE,OAAO,EAAE,MAAM,uBAAuB,qBAAqB,CAC/D;AACF,CAAC;;;;;;;;;;ACfD,SAAgB,kBAAkB,SAAqC;CACrE,OACE,OAAO,YAAY,YACnB,QAAQ,SAAS,KACjB,QAAQ,UAAU,OAClB,YAAY,OACZ,CAAC,QAAQ,SAAS,GAAG,KACrB,CAAC,QAAQ,SAAS,IAAI,KACtB,CAAC,QAAQ,SAAS,IAAI,KACtB,CAAC,QAAQ,SAAS,IAAI;AAE1B;AAEA,SAAgB,sBAAsB,SAAiB,OAAO,gBAAsB;CAClF,IAAI,CAAC,kBAAkB,OAAO,GAC5B,MAAM,IAAI,MAAM,WAAW,KAAK,IAAI,KAAK,UAAU,OAAO,GAAG;AAEjE;;;ACXA,SAAgB,eAAe,MAAoB;CACjD,sBAAsB,MAAM,eAAe;AAC7C;AAEA,SAAgB,eAAe,SAAiB,MAAsB;CACpE,eAAe,IAAI;CACnB,OAAO,KAAA,QAAK,KAAK,SAAS,aAAa,IAAI;AAC7C;AAEA,SAAgB,eAAe,SAAiB,MAAuB;CACrE,OAAO,GAAA,QAAG,WAAW,eAAe,SAAS,IAAI,CAAC;AACpD;;AAGA,SAAgB,kBAAkB,SAA2B;CAC3D,MAAM,MAAM,KAAA,QAAK,KAAK,SAAS,WAAW;CAC1C,IAAI,CAAC,GAAA,QAAG,WAAW,GAAG,GAAG,OAAO,CAAC;CACjC,OAAO,GAAA,QAAG,YAAY,GAAG,EAAE,QAAQ,MAAM;EACvC,IAAI;GACF,OAAO,GAAA,QAAG,SAAS,KAAA,QAAK,KAAK,KAAK,CAAC,CAAC,EAAE,YAAY;EACpD,QAAQ;GACN,OAAO;EACT;CACF,CAAC;AACH;AAEA,eAAsB,kBAAkB,SAAiB,MAA6B;CACpF,MAAM,cAAc,eAAe,SAAS,IAAI;CAChD,GAAA,QAAG,UAAU,aAAa,EAAE,WAAW,KAAK,CAAC;CAC7C,GAAA,QAAG,UAAU,KAAA,QAAK,KAAK,aAAa,aAAa,GAAG,EAAE,WAAW,KAAK,CAAC;CACvE,GAAA,QAAG,UAAU,KAAA,QAAK,KAAK,aAAa,aAAa,GAAG,EAAE,WAAW,KAAK,CAAC;AACzE;AAEA,eAAsB,eACpB,SACA,MACA,OACe;CACf,MAAM,WAAW,KAAA,QAAK,KAAK,eAAe,SAAS,IAAI,GAAG,eAAe;CAEzE,MAAM,QAAQ,OAAO,YACnB,OAAO,QAAQ,KAAgC,EAAE,QAAQ,GAAG,OAAO,MAAM,KAAA,CAAS,CACpF;CAEA,qBAAA,gBAAgB,UADAC,YAAAA,QAAO,UAAU,IAAI,KACL,CAAC;AACnC;AAEA,eAAsB,cAAc,SAAiB,MAAkC;CACrF,MAAM,WAAW,KAAA,QAAK,KAAK,eAAe,SAAS,IAAI,GAAG,eAAe;CACzE,IAAI,CAAC,GAAA,QAAG,WAAW,QAAQ,GACzB,MAAM,IAAI,MAAM,yCAAyC,KAAK,EAAE;CAOlE,MAAM,QAAA,GAAA,YAAA,SAHU,GAAA,QAAG,aAAa,UAAU,OACjB,CAEV,EAAE;CACjB,KAAK,MAAM,OAAO,CAAC,WAAW,SAAS,GACrC,IAAI,KAAK,gBAAgB,MACvB,KAAK,OAAQ,KAAK,KAAc,YAAY,EAAE,MAAM,GAAG,EAAE;CAG7D,MAAM,SAAS,gBAAgB,UAAU,IAAI;CAC7C,IAAI,CAAC,OAAO,SACV,MAAM,IAAI,OAAA,GAAA,qBAAA,cACK,OAAO,OAAO;EACzB,QAAQ,mBAAmB;EAC3B,iBAAiB;EACjB,gBAAgB;CAClB,CAAC,EAAE,OACL;CAEF,OAAO,OAAO;AAChB;;;;;;;;;;;;;;;ACvEA,SAAgB,aAAgB,UAAkB,UAAgB;CAChE,IAAI,CAAC,GAAA,QAAG,WAAW,QAAQ,GAAG,OAAO;CACrC,IAAI;EACF,OAAO,KAAK,MAAM,GAAA,QAAG,aAAa,UAAU,OAAO,CAAW;CAChE,QAAQ;EACN,OAAO;CACT;AACF;;AAGA,SAAgB,cAAc,UAAkB,OAAsB;CACpE,qBAAA,gBAAgB,UAAU,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAC1D;;;;;AAMA,SAAgB,cAAiB,UAAkB,KAAkB;CAEnE,MAAM,MADO,aAAsC,UAAU,CAAC,CAC/C,EAAE;CACjB,OAAO,MAAM,QAAQ,GAAG,IAAK,MAAc,CAAC;AAC9C;;AAGA,SAAgB,eAAkB,UAAkB,KAAa,OAAkB;CACjF,cAAc,UAAU,GAAG,MAAM,MAAM,CAAC;AAC1C;;;ACzCA,MAAa,WAAW,MAAsB,MAAA,QAAM,MAAM,CAAC;AAC3D,MAAa,SAAS,MAAsB,MAAA,QAAM,IAAI,CAAC;AACvD,MAAa,WAAW,MAAsB,MAAA,QAAM,OAAO,CAAC;AAC5D,MAAa,QAAQ,MAAsB,MAAA,QAAM,KAAK,CAAC;AAEvD,MAAa,QAAQ,MAAsB,MAAA,QAAM,KAAK,CAAC;;;ACwCvD,SAAS,cAAc,SAAyB;CAC9C,OAAO,KAAA,QAAK,KAAK,SAAS,YAAY,aAAa;AACrD;AAEA,SAAS,kBAAkB,SAAgC;CACzD,MAAM,WAAW,cAAc,OAAO;CACtC,IAAI,CAAC,GAAA,QAAG,WAAW,QAAQ,GAAG,OAAO,CAAC;CACtC,IAAI;EACF,OAAO,KAAK,MAAM,GAAA,QAAG,aAAa,UAAU,OAAO,CAAW;CAChE,QAAQ;EACN,OAAO,CAAC;CACV;AACF;AAEA,SAAS,mBAAmB,SAAiB,QAA6B;CACxE,cAAc,cAAc,OAAO,GAAG,MAAM;AAC9C;AAIA,SAAS,SAAS,KAA+C;CAC/D,IAAI,QAAQ;CACZ,IAAI,QAAQ;CACZ,IAAI,CAAC,GAAA,QAAG,WAAW,GAAG,GAAG,OAAO;EAAE;EAAO;CAAM;CAC/C,MAAM,QAAQ,MAAc;EAC1B,IAAI;GACF,KAAK,MAAM,SAAS,GAAA,QAAG,YAAY,CAAC,GAAG;IACrC,MAAM,OAAO,KAAA,QAAK,KAAK,GAAG,KAAK;IAC/B,IAAI;KACF,MAAM,OAAO,GAAA,QAAG,SAAS,IAAI;KAC7B,IAAI,KAAK,YAAY,GAAG,KAAK,IAAI;UAC5B;MACH;MACA,SAAS,KAAK;KAChB;IACF,QAAQ,CAER;GACF;EACF,QAAQ,CAER;CACF;CACA,KAAK,GAAG;CACR,OAAO;EAAE;EAAO;CAAM;AACxB;AAEA,SAAS,eAAe,SAAyB;CAC/C,MAAM,MAAM,KAAA,QAAK,KAAK,SAAS,WAAW;CAC1C,IAAI,CAAC,GAAA,QAAG,WAAW,GAAG,GAAG,OAAO;CAChC,IAAI;EACF,OAAO,GAAA,QAAG,YAAY,GAAG,EAAE,QAAQ,MAAM;GACvC,IAAI;IACF,OAAO,GAAA,QAAG,SAAS,KAAA,QAAK,KAAK,KAAK,CAAC,CAAC,EAAE,YAAY;GACpD,QAAQ;IACN,OAAO;GACT;EACF,CAAC,EAAE;CACL,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,WAAW,UAA0B;CAC5C,IAAI,CAAC,GAAA,QAAG,WAAW,QAAQ,GAAG,OAAO;CACrC,MAAM,QAAA,GAAA,OAAA,YAAkB,QAAQ;CAChC,KAAK,OAAO,GAAA,QAAG,aAAa,QAAQ,CAAC;CACrC,OAAO,KAAK,OAAO,KAAK;AAC1B;AAEA,SAAS,cACP,SACA,MACA,SACA,WACgB;CAChB,IAAI,aAAa;CACjB,IAAI,aAAa;CACjB,KAAK,MAAM,KAAK,MAAM;EAEpB,MAAM,EAAE,OAAO,UAAU,SADZ,KAAA,QAAK,KAAK,SAAS,CACK,CAAC;EACtC,cAAc;EACd,cAAc;CAChB;CACA,OAAO;EACL,SAAS;EACT,4BAAW,IAAI,KAAK,GAAE,YAAY;EAClC,cAAc;EACd,aAAa;EACb,eAAe,eAAe,OAAO;EACrC,WAAW;EACX;EACA,QAAQ,WAAW,OAAO;EAC1B;CACF;AACF;AAIA,SAAS,gBAAgB,SAAiB,OAA0B;CAClE,MAAM,UAAU,KAAA,QAAK,KAAK,SAAS,YAAY,iBAAiB;CAChE,IAAI,UAAyB,CAAC;CAC9B,IAAI,GAAA,QAAG,WAAW,OAAO,GACvB,IAAI;EACF,UAAU,KAAK,MAAM,GAAA,QAAG,aAAa,SAAS,OAAO,CAAW;CAClE,QAAQ;EACN,UAAU,CAAC;CACb;CAGF,UAAU,QAAQ,QAAQ,MAAM,EAAE,aAAa,MAAM,QAAQ;CAC7D,QAAQ,QAAQ,KAAK;CAErB,IAAI,QAAQ,SAAS,KAAK,UAAU,QAAQ,MAAM,GAAG,GAAG;CACxD,cAAc,SAAS,OAAO;AAChC;AAEA,SAAgB,cAAc,SAAgC;CAC5D,MAAM,UAAU,KAAA,QAAK,KAAK,SAAS,YAAY,iBAAiB;CAChE,IAAI,CAAC,GAAA,QAAG,WAAW,OAAO,GAAG,OAAO,CAAC;CACrC,IAAI;EACF,OAAO,KAAK,MAAM,GAAA,QAAG,aAAa,SAAS,OAAO,CAAW;CAC/D,QAAQ;EACN,OAAO,CAAC;CACV;AACF;AAIA,eAAsB,UACpB,QACA,SACA,OAA+C,CAAC,GAChB;CAChC,MAAM,MAAM,WAAW,QAAQ,IAAI;CACnC,MAAM,eAAe,KAAA,QAAK,KAAK,KAAK,WAAW;CAE/C,IAAI,CAAC,GAAA,QAAG,WAAW,YAAY,GAAG;EAChC,QAAQ,MAAM,MAAM,iCAAiC,CAAC;EACtD,QAAQ,KAAK,CAAC;CAChB;CAEA,MAAM,UACJ,UAAU,KAAA,QAAK,KAAK,KAAK,iCAAgB,IAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE,EAAE,KAAK;CAGtF,MAAM,cAAc,CAAC,YAAY;CACjC,IAAI,GAAA,QAAG,WAAW,KAAA,QAAK,KAAK,KAAK,UAAU,CAAC,GAC1C,YAAY,KAAK,WAAW;CAG9B,IAAI;EACF,CAAA,GAAA,cAAA,UAAS,WAAW,QAAQ,IAAI,YAAY,KAAK,GAAG,KAAK,EAAE,KAAK,IAAI,CAAC;EAGrE,MAAM,WAAW,cAAc,KAAK,aAAa,SAAS,KAAK,WAAW,KAAK;EAC/E,MAAM,eAAe,KAAA,QAAK,KAAK,KAAK,0BAA0B;EAC9D,GAAA,QAAG,cAAc,cAAc,KAAK,UAAU,UAAU,MAAM,CAAC,GAAG,OAAO;EACzE,IAAI;GACF,CAAA,GAAA,cAAA,UAAS,WAAW,QAAQ,KAAK,aAAa,IAAI,EAAE,KAAK,IAAI,CAAC;EAChE,QAAQ,CAER;EACA,GAAA,QAAG,WAAW,YAAY;EAG1B,MAAM,WAAW,iBAAiB,OAAO;EAYzC,gBAAgB,KAAK;GATnB,UAAU,KAAA,QAAK,SAAS,OAAO;GAC/B,MAAM;GACN,WAAW,SAAS;GACpB,WAAW,GAAA,QAAG,WAAW,OAAO,IAAI,GAAA,QAAG,SAAS,OAAO,EAAE,OAAO;GAChE;GACA,WAAW,KAAK,WAAW;GAC3B,eAAe,SAAS;GACxB,WAAW,SAAS;EAEG,CAAC;EAG1B,IAAI,KAAK,QACP,MAAM,aAAa,SAAS,KAAK,MAAM;EAGzC,QAAQ,IAAI,QAAQ,mBAAmB,SAAS,CAAC;EACjD,QAAQ,IACN,KACE,gBAAgB,SAAS,cAAc,WAAW,SAAS,UAAU,WAAW,SAAS,aAAa,OAAO,MAAM,QAAQ,CAAC,EAAE,IAChI,CACF;EACA,IAAI,CAAC,UAAU,QAAQ,IAAI,KAAK,uDAAuD,CAAC;EAExF,OAAO;CACT,SAAS,KAAK;EACZ,QAAQ,MAAM,MAAM,oBAAqB,IAAc,SAAS,CAAC;EACjE,QAAQ,KAAK,CAAC;CAChB;AACF;AAIA,SAAgB,iBAAiB,SAA0B;CACzD,IAAI,CAAC,GAAA,QAAG,WAAW,OAAO,GAAG,OAAO;CACpC,IAAI;EACF,CAAA,GAAA,cAAA,UAAS,aAAa,QAAQ,IAAI,EAAE,OAAO,OAAO,CAAC;EACnD,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;AAEA,eAAsB,UAAU,SAAgC;CAC9D,IAAI,CAAC,GAAA,QAAG,WAAW,OAAO,GAAG;EAC3B,QAAQ,MAAM,MAAM,qBAAqB,SAAS,CAAC;EACnD,QAAQ,KAAK,CAAC;CAChB;CAEA,QAAQ,IAAI,KAAK,aAAa,KAAA,QAAK,SAAS,OAAO,EAAE,IAAI,CAAC;CAG1D,IAFW,iBAAiB,OAEvB,GAAG;EACN,MAAM,OAAO,GAAA,QAAG,SAAS,OAAO,EAAE;EAClC,MAAM,MAAM,WAAW,OAAO;EAC9B,QAAQ,IAAI,QAAQ,oBAAoB,CAAC;EACzC,QAAQ,IAAI,KAAK,YAAY,OAAO,OAAO,MAAM,QAAQ,CAAC,EAAE,IAAI,CAAC;EACjE,QAAQ,IAAI,KAAK,cAAc,KAAK,CAAC;CACvC,OAAO;EACL,QAAQ,MAAM,MAAM,0BAA0B,CAAC;EAC/C,QAAQ,KAAK,CAAC;CAChB;AACF;AAIA,eAAsB,aAAa,WAAmB,QAA+B;CACnF,IAAI,OAAO,WAAW,OAAO,GAE3B,IAAI;EACF,CAAA,GAAA,cAAA,UAAS,cAAc,UAAU,KAAK,SAAS,KAAA,QAAK,SAAS,SAAS,EAAE,IAAI,EAC1E,OAAO,OACT,CAAC;EACD,QAAQ,IAAI,KAAK,mBAAmB,SAAS,KAAA,QAAK,SAAS,SAAS,GAAG,CAAC;CAC1E,SAAS,KAAK;EACZ,QAAQ,MACN,MACE,iEAAkE,IAAc,SAClF,CACF;CACF;MACK,IAAI,OAAO,WAAW,UAAU,GAAG;EACxC,MAAM,OAAO,OAAO,QAAQ,YAAY,EAAE;EAC1C,IAAI;GACF,CAAA,GAAA,cAAA,UAAS,cAAc,UAAU,KAAK,KAAK,IAAI,EAAE,OAAO,OAAO,CAAC;GAChE,QAAQ,IAAI,KAAK,iBAAiB,MAAM,CAAC;EAC3C,SAAS,KAAK;GACZ,QAAQ,MAAM,MAAM,qBAAsB,IAAc,SAAS,CAAC;EACpE;CACF,OAEE,IAAI;EACF,MAAM,WAAW,KAAA,QAAK,KAAK,QAAQ,KAAA,QAAK,SAAS,SAAS,CAAC;EAC3D,GAAA,QAAG,UAAU,QAAQ,EAAE,WAAW,KAAK,CAAC;EACxC,GAAA,QAAG,aAAa,WAAW,QAAQ;EACnC,QAAQ,IAAI,KAAK,iBAAiB,UAAU,CAAC;CAC/C,SAAS,KAAK;EACZ,QAAQ,MAAM,MAAM,oBAAqB,IAAc,SAAS,CAAC;CACnE;AAEJ;AAIA,SAAgB,iBAAiB,KAA4B;CAC3D,IAAI,CAAC,GAAA,QAAG,WAAW,GAAG,GAAG,OAAO,CAAC;CACjC,IAAI;EACF,OAAO,GAAA,QACJ,YAAY,GAAG,EACf,QAAQ,MAAM,EAAE,MAAM,gCAAgC,CAAC,EACvD,KAAK,MAAM;GACV,MAAM,WAAW,KAAA,QAAK,KAAK,KAAK,CAAC;GACjC,MAAM,OAAO,GAAA,QAAG,SAAS,QAAQ;GACjC,OAAO;IACL,UAAU;IACV,MAAM;IACN,WAAW,KAAK,MAAM,YAAY;IAClC,WAAW,KAAK;IAChB,UAAU;IACV,WAAW,EAAE,SAAS,QAAQ;IAC9B,eAAe;IACf,WAAW;GACb;EACF,CAAC,EACA,MAAM,GAAG,MAAM,EAAE,UAAU,cAAc,EAAE,SAAS,CAAC;CAC1D,QAAQ;EACN,OAAO,CAAC;CACV;AACF;AAiFA,eAAsB,kBACpB,MASA,SACe;CACf,MAAM,MAAM,WAAW,QAAQ,IAAI;CAEnC,IAAI,KAAK,OAAO;EACd,MAAM,SAAS,kBAAkB,GAAG;EACpC,OAAO,OAAO;EACd,mBAAmB,KAAK,MAAM;EAC9B,QAAQ,IAAI,QAAQ,4BAA4B,CAAC;EACjD;CACF;CAEA,IAAI,CAAC,KAAK,SAAS,CAAC,KAAK,QAAQ;EAC/B,QAAQ,MAAM,MAAM,0CAA0C,CAAC;EAC/D,QAAQ,KAAK,CAAC;EACd;CACF;CAEA,IAAI,KAAK,OAAO;EACd,MAAM,OAAO,KAAK,OAAO,SAAS,KAAK,MAAM,EAAE,IAAI;EACnD,MAAM,SAAS,kBAAkB,GAAG;EACpC,OAAO,iBAAiB;GACtB,OAAO,KAAK;GACZ;GACA,GAAI,KAAK,SAAS,EAAE,QAAQ,SAAS,KAAK,QAAQ,EAAE,EAAE,IAAI,CAAC;GAC3D,GAAI,KAAK,UAAU,EAAE,SAAS,SAAS,KAAK,SAAS,EAAE,EAAE,IAAI,CAAC;GAC9D,GAAI,KAAK,SAAS,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;GAC7C,YAAY;EACd;EACA,mBAAmB,KAAK,MAAM;EAC9B,IAAI,CAAC,KAAK,QACR,QAAQ,IACN,QACE,gCAAgC,KAAK,MAAM,SAAS,KAAK,QAAQ,KAAK,SAAS,MAAM,KAAK,OAAO,WAAW,KAAK,KAAK,UAAU,MAAM,KAAK,QAAQ,YAAY,GAAG,EACpK,CACF;CAEJ;CAEA,IAAI,KAAK,QAAQ;EAEf,MAAM,QADS,kBAAkB,GACd,EAAE;EACrB,IAAI,CAAC,OACH,QAAQ,IAAI,KAAK,gCAAgC,CAAC;OAC7C;GACL,QAAQ,IAAI,KAAK,kBAAkB,CAAC;GACpC,QAAQ,IAAI,iBAAiB,MAAM,OAAO;GAC1C,QAAQ,IAAI,iBAAiB,MAAM,KAAK,eAAe;GACvD,IAAI,MAAM,QAAQ,QAAQ,IAAI,iBAAiB,MAAM,OAAO,gBAAgB;GAC5E,IAAI,MAAM,SAAS,QAAQ,IAAI,iBAAiB,MAAM,QAAQ,iBAAiB;GAC/E,IAAI,MAAM,QAAQ,QAAQ,IAAI,iBAAiB,MAAM,QAAQ;GAC7D,QAAQ,IAAI,iBAAiB,MAAM,cAAc,SAAS;EAC5D;CACF;AACF;AAoDA,eAAsB,WAAW,SAAiB,SAAiC;CACjF,MAAM,MAAM,WAAW,QAAQ,IAAI;CACnC,IAAI;EACF,CAAA,GAAA,cAAA,UAAS,aAAa,KAAA,QAAK,QAAQ,OAAO,EAAE,QAAQ,IAAI,IAAI,EAAE,KAAK,IAAI,CAAC;EACxE,QAAQ,IAAI,QAAQ,qBAAqB,CAAC;CAC5C,SAAS,KAAK;EACZ,QAAQ,MAAM,MAAM,qBAAsB,IAAc,SAAS,CAAC;EAClE,QAAQ,KAAK,CAAC;CAChB;AACF;;;;;;AAeA,eAAsB,gBACpB,SACA,OAA6B,CAAC,GACD;CAC7B,MAAM,WAAW,KAAA,QAAK,QAAQ,OAAO;CACrC,IAAI,CAAC,GAAA,QAAG,WAAW,QAAQ,GAAG;EAC5B,IAAI,CAAC,KAAK,QAAQ,QAAQ,MAAM,MAAM,qBAAqB,SAAS,CAAC;EACrE,OAAO;GACL,IAAI;GACJ,UAAU;GACV,cAAc;GACd,YAAY;GACZ,QAAQ;EACV;CACF;CAEA,MAAM,WAAW,iBAAiB,QAAQ;CAC1C,IAAI,eAAe;CACnB,IAAI,aAAa;CACjB,IAAI,UACF,IAAI;EACF,MAAM,WAAA,GAAA,cAAA,UAAmB,aAAa,SAAS,IAAI,EAAE,OAAO,OAAO,CAAC,EAAE,SAAS;EAC/E,eAAe,QAAQ,SAAS,YAAY;EAC5C,aAAa,QAAQ,SAAS,WAAW;CAC3C,QAAQ,CAER;CAGF,MAAM,KAAK,YAAY;CACvB,IAAI,CAAC,KAAK,QACR,IAAI,IACF,QAAQ,IACN,QACE,sDAAsD,aAAa,iBAAiB,GAAG,SACzF,CACF;MAEA,QAAQ,MACN,MAAM,oCAAoC,SAAS,cAAc,aAAa,EAAE,CAClF;CAGJ,OAAO;EAAE;EAAI;EAAU;EAAc;CAAW;AAClD;AAIA,MAAM,qBAAqB,IAAIC,UAAAA,QAAQ,UAAU,EAC9C,YAAY,qCAAqC,EACjD,OAAO,sBAAsB,4BAA4B,EACzD,OAAO,cAAc,oCAAoC,EACzD,OAAO,gBAAgB,iCAAiC,EACxD,OAAO,iBAAiB,mCAAmC,EAC3D,OAAO,kBAAkB,qDAAqD,EAC9E,OAAO,YAAY,uBAAuB,EAC1C,OAAO,WAAW,wBAAwB,EAC1C,QAAQ,SAAS,kBAAkB,MAAM,QAAQ,IAAI,qBAAqB,QAAQ,IAAI,CAAC,CAAC;AAE3F,MAAM,mBAAmB,IAAIA,UAAAA,QAAQ,QAAQ,EAC1C,SAAS,UAAU,oBAAoB,EACvC,YAAY,8CAA8C,EAC1D,QAAQ,YAAoB,UAAU,OAAO,CAAC;AAEjD,MAAM,kBAAkB,IAAIA,UAAAA,QAAQ,OAAO,EACxC,SAAS,UAAU,oBAAoB,EACvC,YAAY,yEAAyE,EACrF,OAAO,OAAO,YAAoB;CAEjC,IAAI,EAAC,MADgB,gBAAgB,OAAO,GAChC,IAAI,QAAQ,WAAW;AACrC,CAAC;AAEH,MAAM,iBAAiB,IAAIA,UAAAA,QAAQ,MAAM,EAAE,YAAY,wBAAwB,EAAE,aAAa;CAC5F,MAAM,MAAM,QAAQ,IAAI,qBAAqB,QAAQ,IAAI;CACzD,MAAM,UAAU,cAAc,GAAG;CACjC,MAAM,cAAc,iBAAiB,GAAG;CACxC,MAAM,WAAW,QAAQ,SAAS,IAAI,UAAU;CAChD,IAAI,SAAS,WAAW,GAAG;EACzB,QAAQ,IAAI,KAAK,mBAAmB,CAAC;EACrC;CACF;CACA,KAAK,MAAM,KAAK,UAAU;EACxB,MAAM,MAAM,EAAE,YAAY,iBAAiB;EAC3C,MAAM,MAAM,EAAE,WAAW,OAAO;EAChC,MAAM,KAAK,EAAE,YAAY,IAAI,KAAK,EAAE,YAAY,OAAO,MAAM,QAAQ,CAAC,EAAE,OAAO;EAC/E,QAAQ,IAAI,KAAK,KAAK,EAAE,QAAQ,IAAI,MAAM,MAAM,GAAG,IAAI,EAAE,UAAU,MAAM,GAAG,EAAE,GAAG;CACnF;AACF,CAAC;AAED,MAAa,gBAAgB,IAAIA,UAAAA,QAAQ,QAAQ,EAC9C,SAAS,YAAY,4BAA4B,EACjD,YAAY,2CAA2C,EACvD,OAAO,aAAa,kCAAkC,EACtD,OAAO,kBAAkB,kDAAkD,EAC3E,QAAQ,QAAiB,SAAkD;CAC1E,UAAe,QAAQ,QAAQ,IAAI,qBAAqB,QAAQ,IAAI,GAAG,QAAQ,CAAC,CAAC;AACnF,CAAC;AAEH,cAAc,WAAW,kBAAkB;AAC3C,cAAc,WAAW,gBAAgB;AACzC,cAAc,WAAW,eAAe;AACxC,cAAc,WAAW,cAAc;AAET,IAAIA,UAAAA,QAAQ,SAAS,EAChD,SAAS,UAAU,oBAAoB,EACvC,YAAY,yBAAyB,EACrC,QAAQ,YAAoB,WAAW,SAAS,QAAQ,IAAI,qBAAqB,QAAQ,IAAI,CAAC,CAAC;;;ACjpBlG,MAAM,iBAAiB;AAEvB,SAAgB,WAAmB;CACjC,MAAM,QAAQ,QAAQ,IAAI;CAC1B,OAAO,SAAS,MAAM,KAAK,EAAE,SAAS,IAAI,MAAM,KAAK,IAAI;AAC3D;AAEA,SAAgB,gBAAgB,SAAiB,OAAyB;CACxE,MAAM,UAAU,KAAA,QAAK,KAAK,SAAS,cAAc;CACjD,MAAM,SAAS,KAAA,QAAK,QAAQ,OAAO;CAEnC,IAAI,CAAC,GAAA,QAAG,WAAW,MAAM,GACvB,GAAA,QAAG,UAAU,QAAQ,EAAE,WAAW,KAAK,CAAC;CAG1C,MAAM,mBAAmB,MAAM,QAAQ,MAAM,GAAG,GAAG;CACnD,MAAM,OAAO,GAAG,MAAM,UAAU,KAAK,MAAM,MAAM,KAAK,MAAM,KAAK,KAAK,MAAM,KAAK,KAAK,iBAAiB;CAEvG,GAAA,QAAG,eAAe,SAAS,MAAM,OAAO;AAC1C;AAEA,SAAgB,aAAa,SAA+B;CAC1D,MAAM,UAAU,KAAA,QAAK,KAAK,SAAS,cAAc;CAEjD,IAAI,CAAC,GAAA,QAAG,WAAW,OAAO,GACxB,OAAO,CAAC;CAIV,MAAM,QADU,GAAA,QAAG,aAAa,SAAS,OACrB,EAAE,MAAM,IAAI;CAEhC,MAAM,UAAwB,CAAC;CAC/B,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,UAAU,KAAK,KAAK;EAC1B,IAAI,CAAC,SAAS;EAEd,MAAM,QAAQ,QAAQ,MAAM,KAAK;EACjC,IAAI,MAAM,SAAS,GAAG;EAEtB,MAAM,CAAC,WAAW,OAAO,MAAM,MAAM,GAAG,gBAAgB;EACxD,MAAM,UAAU,aAAa,KAAK,KAAK;EAEvC,IAAI,aAAa,SAAS,QAAQ,MAChC,QAAQ,KAAK;GAAE;GAAW;GAAO;GAAM;GAAM,SAAS,WAAW;EAAG,CAAC;CAEzE;CAEA,OAAO;AACT;AAEA,SAAgB,eACd,SACA,MACc;CACd,IAAI,WAAW;CAEf,IAAI,KAAK,SAAS,KAAA,GAChB,WAAW,SAAS,QAAQ,MAAM,EAAE,SAAS,KAAK,IAAI;CAGxD,IAAI,KAAK,UAAU,KAAA,GACjB,WAAW,SAAS,QAAQ,MAAM,EAAE,UAAU,KAAK,KAAK;CAG1D,IAAI,KAAK,UAAU,KAAA,GACjB,WAAW,SAAS,MAAM,CAAC,KAAK,KAAK;CAGvC,OAAO;AACT;;;ACrEA,MAAM,gBAAwC;CAC5C,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;CACA,SAAS;EAAC;EAAmB;EAAe;EAAe;CAAe;CAC1E,KAAK;EAAC;EAAmB;EAAe;CAAe;AACzD;AAEA,SAAS,SAAS,SAAyB;CACzC,OAAO,KAAA,QAAK,KAAK,SAAS,YAAY,WAAW;AACnD;AAEA,SAAgB,cAAc,SAA6B;CACzD,OAAO,aAAyB,SAAS,OAAO,GAAG,EAAE,QAAQ,CAAC,EAAE,CAAC;AACnE;AAQA,SAAgB,QAAQ,SAAiB,OAAqB;CAC5D,MAAM,SAAS,cAAc,OAAO;CACpC,OAAO,OAAO,OAAO,UAAU,OAAO,WAAW;AACnD;AAEA,SAAgB,SAAS,MAAY,MAAuB;CAC1D,OAAO,cAAc,OAAO,SAAS,IAAI,KAAK;AAChD;AAEA,SAAgB,eAAe,MAAY,MAAc,OAAqB;CAC5E,IAAI,CAAC,SAAS,MAAM,IAAI,GACtB,MAAM,IAAI,MAAM,mBAAmB,MAAM,WAAW,KAAK,qBAAqB,KAAK,EAAE;AAEzF;AAEA,SAAgB,YAAY,SAAiB,MAAoB;CAC/D,IAAI,CAAC,GAAA,QAAG,WAAW,SAAS,OAAO,CAAC,GAAG;CACvC,MAAM,QAAQ,QAAQ,IAAI,kBAAkB;CAC5C,IAAI,UAAU,UAAU;CAExB,eADa,QAAQ,SAAS,KACZ,GAAG,MAAM,KAAK;AAClC;AAEA,SAAgB,eAAe,SAAiB,OAAe,MAAuB;CACpF,IAAI,CAAC,GAAA,QAAG,WAAW,SAAS,OAAO,CAAC,GAAG,OAAO;CAC9C,IAAI,UAAU,UAAU,OAAO;CAC/B,MAAM,SAAS,cAAc,OAAO;CACpC,MAAM,OAAO,OAAO,OAAO,UAAU,OAAO,WAAW;CACvD,IAAI,SAAS,WAAW,SAAS,WAAW,OAAO;CAEnD,MAAM,QAAQ,OAAO;CACrB,IAAI,CAAC,OAAO,OAAO;CACnB,QAAQ,MAAM,UAAU,CAAC,GAAG,SAAS,IAAI;AAC3C;;;;;;AAOA,SAAgB,mBAAmB,SAAiB,OAA0C;CAC5F,IAAI,CAAC,GAAA,QAAG,WAAW,SAAS,OAAO,CAAC,GAAG,aAAa;CACpD,IAAI,UAAU,UAAU,aAAa;CACrC,MAAM,SAAS,cAAc,OAAO;CACpC,MAAM,OAAO,OAAO,OAAO,UAAU,OAAO,WAAW;CACvD,IAAI,SAAS,WAAW,SAAS,WAAW,aAAa;CACzD,MAAM,QAAQ,IAAI,IAAI,OAAO,kBAAkB,UAAU,CAAC,CAAC;CAC3D,QAAQ,SAAiB,MAAM,IAAI,IAAI;AACzC;;;ACpFA,IAAI,gBAAgC;AAEpC,SAAgB,WAAW,GAAkB;CAC3C,gBAAgB;AAClB;AAEA,SAAgB,aAA6B;CAC3C,OAAO;AACT;AAEA,SAAgB,eAAqB;CACnC,gBAAgB;AAClB"}
@@ -1,2 +1,2 @@
1
- import { i as loadSlaRules, n as checkSlaBreaches, t as calcSlaDue } from "./sla-engine-BqX-7u-7.js";
1
+ import { i as loadSlaRules, n as checkSlaBreaches, t as calcSlaDue } from "./sla-engine-O-A1ntu_.js";
2
2
  export { calcSlaDue, checkSlaBreaches, loadSlaRules };
@@ -44,10 +44,10 @@ function isSlaBreach(ticket, today) {
44
44
  return ticket.slaDue < today;
45
45
  }
46
46
  async function checkSlaBreaches(dataDir, today) {
47
- const { listAllTickets } = await import("./ticket-writer-CjqKeIRD.js");
47
+ const { listAllTickets } = await import("./ticket-writer-DsfpeLGZ.js");
48
48
  return (await listAllTickets(dataDir)).filter(({ ticket }) => isSlaBreach(ticket, today));
49
49
  }
50
50
  //#endregion
51
51
  export { loadSlaRules as i, checkSlaBreaches as n, isSlaBreach as r, calcSlaDue as t };
52
52
 
53
- //# sourceMappingURL=sla-engine-BqX-7u-7.js.map
53
+ //# sourceMappingURL=sla-engine-O-A1ntu_.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sla-engine-BqX-7u-7.js","names":[],"sources":["../src/core/sla-engine.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\nimport yaml from \"js-yaml\";\nimport type { Ticket } from \"../schemas/ticket.js\";\n\nexport interface SlaRule {\n priority: Ticket[\"priority\"];\n resolveDays: number;\n}\n\nconst DEFAULT_RULES: SlaRule[] = [\n { priority: \"urgent\", resolveDays: 1 },\n { priority: \"high\", resolveDays: 2 },\n { priority: \"normal\", resolveDays: 5 },\n { priority: \"low\", resolveDays: 10 },\n];\n\nexport function loadSlaRules(dataDir: string): SlaRule[] {\n const p = path.join(dataDir, \".agentic\", \"sla-rules.yaml\");\n if (!fs.existsSync(p)) return DEFAULT_RULES;\n try {\n const raw = yaml.load(fs.readFileSync(p, \"utf-8\") as string) as { rules?: SlaRule[] };\n return raw?.rules ?? DEFAULT_RULES;\n } catch {\n return DEFAULT_RULES;\n }\n}\n\nfunction addDaysToDate(isoDate: string, days: number): string {\n const [year, month, day] = isoDate.split(\"-\").map(Number) as [number, number, number];\n const d = new Date(Date.UTC(year, month - 1, day));\n d.setUTCDate(d.getUTCDate() + days);\n return d.toISOString().slice(0, 10);\n}\n\nexport function calcSlaDue(\n createdDate: string,\n priority: Ticket[\"priority\"],\n rules: SlaRule[]\n): string {\n const rule = rules.find((r) => r.priority === priority) ?? { resolveDays: 5 };\n return addDaysToDate(createdDate, rule.resolveDays);\n}\n\nexport function isSlaBreach(ticket: Ticket, today: string): boolean {\n if (ticket.status === \"resolved\" || ticket.status === \"closed\") return false;\n if (!ticket.slaDue) return false;\n return ticket.slaDue < today;\n}\n\nexport async function checkSlaBreaches(\n dataDir: string,\n today: string\n): Promise<Array<{ slug: string; ticket: Ticket }>> {\n const { listAllTickets } = await import(\"../fs/ticket-writer.js\");\n const all = await listAllTickets(dataDir);\n return all.filter(({ ticket }) => isSlaBreach(ticket, today));\n}\n"],"mappings":";;;;AAUA,MAAM,gBAA2B;CAC/B;EAAE,UAAU;EAAU,aAAa;CAAE;CACrC;EAAE,UAAU;EAAQ,aAAa;CAAE;CACnC;EAAE,UAAU;EAAU,aAAa;CAAE;CACrC;EAAE,UAAU;EAAO,aAAa;CAAG;AACrC;AAEA,SAAgB,aAAa,SAA4B;CACvD,MAAM,IAAI,KAAK,KAAK,SAAS,YAAY,gBAAgB;CACzD,IAAI,CAAC,GAAG,WAAW,CAAC,GAAG,OAAO;CAC9B,IAAI;EAEF,OADY,KAAK,KAAK,GAAG,aAAa,GAAG,OAAO,CACvC,GAAG,SAAS;CACvB,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,cAAc,SAAiB,MAAsB;CAC5D,MAAM,CAAC,MAAM,OAAO,OAAO,QAAQ,MAAM,GAAG,EAAE,IAAI,MAAM;CACxD,MAAM,IAAI,IAAI,KAAK,KAAK,IAAI,MAAM,QAAQ,GAAG,GAAG,CAAC;CACjD,EAAE,WAAW,EAAE,WAAW,IAAI,IAAI;CAClC,OAAO,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AACpC;AAEA,SAAgB,WACd,aACA,UACA,OACQ;CAER,OAAO,cAAc,cADR,MAAM,MAAM,MAAM,EAAE,aAAa,QAAQ,KAAK,EAAE,aAAa,EAAE,GACrC,WAAW;AACpD;AAEA,SAAgB,YAAY,QAAgB,OAAwB;CAClE,IAAI,OAAO,WAAW,cAAc,OAAO,WAAW,UAAU,OAAO;CACvE,IAAI,CAAC,OAAO,QAAQ,OAAO;CAC3B,OAAO,OAAO,SAAS;AACzB;AAEA,eAAsB,iBACpB,SACA,OACkD;CAClD,MAAM,EAAE,mBAAmB,MAAM,OAAO;CAExC,QAAO,MADW,eAAe,OAAO,GAC7B,QAAQ,EAAE,aAAa,YAAY,QAAQ,KAAK,CAAC;AAC9D"}
1
+ {"version":3,"file":"sla-engine-O-A1ntu_.js","names":[],"sources":["../src/core/sla-engine.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\nimport yaml from \"js-yaml\";\nimport type { Ticket } from \"../schemas/ticket.js\";\n\nexport interface SlaRule {\n priority: Ticket[\"priority\"];\n resolveDays: number;\n}\n\nconst DEFAULT_RULES: SlaRule[] = [\n { priority: \"urgent\", resolveDays: 1 },\n { priority: \"high\", resolveDays: 2 },\n { priority: \"normal\", resolveDays: 5 },\n { priority: \"low\", resolveDays: 10 },\n];\n\nexport function loadSlaRules(dataDir: string): SlaRule[] {\n const p = path.join(dataDir, \".agentic\", \"sla-rules.yaml\");\n if (!fs.existsSync(p)) return DEFAULT_RULES;\n try {\n const raw = yaml.load(fs.readFileSync(p, \"utf-8\") as string) as { rules?: SlaRule[] };\n return raw?.rules ?? DEFAULT_RULES;\n } catch {\n return DEFAULT_RULES;\n }\n}\n\nfunction addDaysToDate(isoDate: string, days: number): string {\n const [year, month, day] = isoDate.split(\"-\").map(Number) as [number, number, number];\n const d = new Date(Date.UTC(year, month - 1, day));\n d.setUTCDate(d.getUTCDate() + days);\n return d.toISOString().slice(0, 10);\n}\n\nexport function calcSlaDue(\n createdDate: string,\n priority: Ticket[\"priority\"],\n rules: SlaRule[]\n): string {\n const rule = rules.find((r) => r.priority === priority) ?? { resolveDays: 5 };\n return addDaysToDate(createdDate, rule.resolveDays);\n}\n\nexport function isSlaBreach(ticket: Ticket, today: string): boolean {\n if (ticket.status === \"resolved\" || ticket.status === \"closed\") return false;\n if (!ticket.slaDue) return false;\n return ticket.slaDue < today;\n}\n\nexport async function checkSlaBreaches(\n dataDir: string,\n today: string\n): Promise<Array<{ slug: string; ticket: Ticket }>> {\n const { listAllTickets } = await import(\"../fs/ticket-writer.js\");\n const all = await listAllTickets(dataDir);\n return all.filter(({ ticket }) => isSlaBreach(ticket, today));\n}\n"],"mappings":";;;;AAUA,MAAM,gBAA2B;CAC/B;EAAE,UAAU;EAAU,aAAa;CAAE;CACrC;EAAE,UAAU;EAAQ,aAAa;CAAE;CACnC;EAAE,UAAU;EAAU,aAAa;CAAE;CACrC;EAAE,UAAU;EAAO,aAAa;CAAG;AACrC;AAEA,SAAgB,aAAa,SAA4B;CACvD,MAAM,IAAI,KAAK,KAAK,SAAS,YAAY,gBAAgB;CACzD,IAAI,CAAC,GAAG,WAAW,CAAC,GAAG,OAAO;CAC9B,IAAI;EAEF,OADY,KAAK,KAAK,GAAG,aAAa,GAAG,OAAO,CACvC,GAAG,SAAS;CACvB,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,cAAc,SAAiB,MAAsB;CAC5D,MAAM,CAAC,MAAM,OAAO,OAAO,QAAQ,MAAM,GAAG,EAAE,IAAI,MAAM;CACxD,MAAM,IAAI,IAAI,KAAK,KAAK,IAAI,MAAM,QAAQ,GAAG,GAAG,CAAC;CACjD,EAAE,WAAW,EAAE,WAAW,IAAI,IAAI;CAClC,OAAO,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AACpC;AAEA,SAAgB,WACd,aACA,UACA,OACQ;CAER,OAAO,cAAc,cADR,MAAM,MAAM,MAAM,EAAE,aAAa,QAAQ,KAAK,EAAE,aAAa,EAAE,GACrC,WAAW;AACpD;AAEA,SAAgB,YAAY,QAAgB,OAAwB;CAClE,IAAI,OAAO,WAAW,cAAc,OAAO,WAAW,UAAU,OAAO;CACvE,IAAI,CAAC,OAAO,QAAQ,OAAO;CAC3B,OAAO,OAAO,SAAS;AACzB;AAEA,eAAsB,iBACpB,SACA,OACkD;CAClD,MAAM,EAAE,mBAAmB,MAAM,OAAO;CAExC,QAAO,MADW,eAAe,OAAO,GAC7B,QAAQ,EAAE,aAAa,YAAY,QAAQ,KAAK,CAAC;AAC9D"}
@@ -1,6 +1,6 @@
1
+ import { r as writeJsonArray, t as readJsonArray } from "./json-store-WWsFzXub.js";
1
2
  import { t as hybridSearch } from "./hybrid-search-BmHttLrR.js";
2
3
  import path from "path";
3
- import fs from "fs";
4
4
  import { randomBytes } from "crypto";
5
5
  //#region src/core/sop.ts
6
6
  function globalPath(dataDir) {
@@ -10,17 +10,10 @@ function customerPath(dataDir, slug) {
10
10
  return path.join(dataDir, "customers", slug, "sops.json");
11
11
  }
12
12
  function readFile(p) {
13
- if (!fs.existsSync(p)) return [];
14
- try {
15
- const data = JSON.parse(fs.readFileSync(p, "utf-8"));
16
- return Array.isArray(data.sops) ? data.sops : [];
17
- } catch {
18
- return [];
19
- }
13
+ return readJsonArray(p, "sops");
20
14
  }
21
15
  function writeFile(p, sops) {
22
- fs.mkdirSync(path.dirname(p), { recursive: true });
23
- fs.writeFileSync(p, JSON.stringify({ sops }, null, 2), "utf-8");
16
+ writeJsonArray(p, "sops", sops);
24
17
  }
25
18
  function addSop(dataDir, s) {
26
19
  const sop = {
@@ -67,4 +60,4 @@ async function findSops(dataDir, query, slug) {
67
60
  //#endregion
68
61
  export { findSops as n, loadSops as r, addSop as t };
69
62
 
70
- //# sourceMappingURL=sop-Vp0UPWFW.js.map
63
+ //# sourceMappingURL=sop-BV7ICAFR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sop-BV7ICAFR.js","names":[],"sources":["../src/core/sop.ts"],"sourcesContent":["import { randomBytes } from \"crypto\";\nimport path from \"path\";\nimport { hybridSearch } from \"./hybrid-search.js\";\nimport { readJsonArray, writeJsonArray } from \"../fs/json-store.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 return readJsonArray<Sop>(p, \"sops\");\n}\nfunction writeFile(p: string, sops: Sop[]): void {\n writeJsonArray(p, \"sops\", sops);\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,OAAO,cAAmB,GAAG,MAAM;AACrC;AACA,SAAS,UAAU,GAAW,MAAmB;CAC/C,eAAe,GAAG,QAAQ,IAAI;AAChC;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 +1,2 @@
1
- import { n as findSops, r as loadSops, t as addSop } from "./sop-Vp0UPWFW.js";
1
+ import { n as findSops, r as loadSops, t as addSop } from "./sop-BV7ICAFR.js";
2
2
  export { addSop, findSops, loadSops };
@@ -0,0 +1,2 @@
1
+ import { s as recordSurveyResponse } from "./survey-engine-DngXBv47.js";
2
+ export { recordSurveyResponse };
@@ -1,3 +1,4 @@
1
+ import { t as writeFileAtomic } from "./atomic-write-8yjqqLtS.js";
1
2
  import path from "path";
2
3
  import fs from "fs";
3
4
  import { z } from "zod";
@@ -56,7 +57,7 @@ function getSurvey(dataDir, surveyId) {
56
57
  function writeSurvey(dataDir, survey) {
57
58
  const dir = surveysDir(dataDir);
58
59
  fs.mkdirSync(dir, { recursive: true });
59
- fs.writeFileSync(path.join(dir, `${survey.id}.yaml`), yaml.dump(survey), "utf-8");
60
+ writeFileAtomic(path.join(dir, `${survey.id}.yaml`), yaml.dump(survey));
60
61
  }
61
62
  function listSurveys(dataDir) {
62
63
  const dir = surveysDir(dataDir);
@@ -99,7 +100,7 @@ async function recordSurveyResponse(dataDir, token, score, comment) {
99
100
  const dir = responsesDir(dataDir, pending.surveyId);
100
101
  fs.mkdirSync(dir, { recursive: true });
101
102
  const filename = `${pending.slug}_${pending.contactEmail.replace("@", "_at_")}_${Date.now()}.json`;
102
- fs.writeFileSync(path.join(dir, filename), JSON.stringify(response, null, 2), "utf-8");
103
+ writeFileAtomic(path.join(dir, filename), JSON.stringify(response, null, 2));
103
104
  fs.unlinkSync(path.join(pendingDir, file));
104
105
  return response;
105
106
  } catch {
@@ -139,9 +140,9 @@ async function savePendingSurvey(dataDir, surveyId, slug, contactEmail, token) {
139
140
  contactEmail,
140
141
  sentAt: (/* @__PURE__ */ new Date()).toISOString()
141
142
  };
142
- fs.writeFileSync(path.join(pendingDir, filename), JSON.stringify(pending, null, 2), "utf-8");
143
+ writeFileAtomic(path.join(pendingDir, filename), JSON.stringify(pending, null, 2));
143
144
  }
144
145
  //#endregion
145
146
  export { listSurveys as a, responsesDir as c, writeSurvey as d, getSurvey as i, savePendingSurvey as l, calcNpsScore as n, loadSurveyResponses as o, generateSurveyToken as r, recordSurveyResponse as s, buildSurveyEmail as t, surveysDir as u };
146
147
 
147
- //# sourceMappingURL=survey-engine-DBjCYqCv.js.map
148
+ //# sourceMappingURL=survey-engine-DngXBv47.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"survey-engine-DngXBv47.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 { writeFileAtomic } from \"../fs/atomic-write.js\";\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 writeFileAtomic(path.join(dir, `${survey.id}.yaml`), yaml.dump(survey));\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 writeFileAtomic(path.join(dir, filename), JSON.stringify(response, null, 2));\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 writeFileAtomic(path.join(pendingDir, filename), JSON.stringify(pending, null, 2));\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import { r as updateSlugSyncState, t as getLastGmailSync } from "./sync-state-ChaLbamC.js";
1
+ import { r as updateSlugSyncState, t as getLastGmailSync } from "./sync-state-DMZgzpez.js";
2
2
  export { getLastGmailSync, updateSlugSyncState };
@@ -1,27 +1,19 @@
1
+ import { i as writeJsonFile, n as readJsonFile } from "./json-store-WWsFzXub.js";
1
2
  import path from "path";
2
- import fs from "fs";
3
3
  //#region src/fs/sync-state.ts
4
4
  function getSyncStatePath(dataDir) {
5
5
  return path.join(dataDir, ".agentic", "sync-state.json");
6
6
  }
7
7
  function readSyncState(dataDir) {
8
- const filePath = getSyncStatePath(dataDir);
9
- if (!fs.existsSync(filePath)) return {};
10
- try {
11
- return JSON.parse(fs.readFileSync(filePath, "utf-8"));
12
- } catch {
13
- return {};
14
- }
8
+ return readJsonFile(getSyncStatePath(dataDir), {});
15
9
  }
16
10
  function updateSlugSyncState(dataDir, slug, update) {
17
- const filePath = getSyncStatePath(dataDir);
18
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
19
11
  const state = readSyncState(dataDir);
20
12
  state[slug] = {
21
13
  ...state[slug],
22
14
  ...update
23
15
  };
24
- fs.writeFileSync(filePath, JSON.stringify(state, null, 2), "utf-8");
16
+ writeJsonFile(getSyncStatePath(dataDir), state);
25
17
  }
26
18
  function getLastGmailSync(dataDir, slug) {
27
19
  const ts = readSyncState(dataDir)[slug]?.lastGmailSync;
@@ -30,4 +22,4 @@ function getLastGmailSync(dataDir, slug) {
30
22
  //#endregion
31
23
  export { readSyncState as n, updateSlugSyncState as r, getLastGmailSync as t };
32
24
 
33
- //# sourceMappingURL=sync-state-ChaLbamC.js.map
25
+ //# sourceMappingURL=sync-state-DMZgzpez.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-state-DMZgzpez.js","names":[],"sources":["../src/fs/sync-state.ts"],"sourcesContent":["import path from \"path\";\nimport { readJsonFile, writeJsonFile } from \"./json-store.js\";\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 return readJsonFile<SyncState>(getSyncStatePath(dataDir), {});\n}\n\nexport function writeSyncState(dataDir: string, state: SyncState): void {\n writeJsonFile(getSyncStatePath(dataDir), state);\n}\n\nexport function updateSlugSyncState(\n dataDir: string,\n slug: string,\n update: Partial<SlugSyncState>\n): void {\n const state = readSyncState(dataDir);\n state[slug] = { ...state[slug], ...update };\n writeJsonFile(getSyncStatePath(dataDir), state);\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,OAAO,aAAwB,iBAAiB,OAAO,GAAG,CAAC,CAAC;AAC9D;AAMA,SAAgB,oBACd,SACA,MACA,QACM;CACN,MAAM,QAAQ,cAAc,OAAO;CACnC,MAAM,QAAQ;EAAE,GAAG,MAAM;EAAO,GAAG;CAAO;CAC1C,cAAc,iBAAiB,OAAO,GAAG,KAAK;AAChD;AAEA,SAAgB,iBAAiB,SAAiB,MAAgC;CAChF,MAAM,KAAK,cAAc,OAAO,EAAE,OAAO;CACzC,OAAO,KAAK,IAAI,KAAK,EAAE,IAAI,KAAA;AAC7B"}
@@ -1,2 +1,2 @@
1
- import { i as upsertTicket, n as nextTicketId, r as readTickets, t as listAllTickets } from "./ticket-writer-j2oX_Wal.js";
1
+ import { i as upsertTicket, n as nextTicketId, r as readTickets, t as listAllTickets } from "./ticket-writer-a9on36Wb.js";
2
2
  export { listAllTickets, nextTicketId, readTickets, upsertTicket };